diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..1f0809a21 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +**NOTE**: (PR title should be of the form ticket:short description, e.g. TICKET-123: Add more templates) + +**JIRA ticket**: [TICKET-123](https://link.to.jira) + +**Summary**: + +A brief description of what this pull request aims to achieve + +**Changes**: +- List changes made in this pull request + +**To test**: +- List actions that should be taken to test that functionality works as expected diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..709c7f49c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,88 @@ +name: Build and Test SynchWeb + +on: + push: + branches: + - 'master' + - 'prerelease' + - 'release/**' + - 'pre-release/**' + pull_request: + branches: + - 'master' + - 'prerelease' + - 'release/**' + - 'pre-release/**' + +defaults: + run: + shell: bash + working-directory: ./api + +# Note, jobs do not share the same working environment, whereas steps do. Also, jobs will run in parallel unless the 'needs' tag is used to flag a dependency +jobs: + php-build: + name: Checkout, build, test and lint PHP code + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PHP 7.3 + uses: shivammathur/setup-php@v2 + with: + php-version: 7.3 + tools: psalm:4 + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: PHPUnit Tests + uses: php-actions/phpunit@v3 + env: + XDEBUG_MODE: coverage + with: + bootstrap: api/vendor/autoload.php + configuration: api/tests/phpunit.xml + php_extensions: xdebug mysqli zip + args: --coverage-text + php_version: 7.3 + version: 9 + + - name: Run Psalm + run: psalm --output-format=github + + js_build: + name: JavaScript build, test and lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Use Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: JavaScript build, lint and test + working-directory: ./client + # hack the output from the linting steps to avoid these stopping the builds - we are not going to get + # to a clean output without considerable effort, but it's useful to see the output + run: | + node --version + npm ci + npm run build + npm run test + npm run lint || exit 0 + npm run lint-vue || exit 0 diff --git a/.github/workflows/php_ci.yml b/.github/workflows/php_ci.yml deleted file mode 100644 index 6c5272354..000000000 --- a/.github/workflows/php_ci.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Build and Test PHP - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -defaults: - run: - shell: bash - working-directory: api - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 5.4 - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v3 - with: - path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- - - - name: Install dependencies - run: composer install --prefer-dist --no-progress - - phplint: - needs: build - runs-on: ubuntu-latest - steps: - - name: Psalm Linting - uses: docker://vimeo/psalm-github-actions - with: - security_analysis: true - - phptest: - needs: build - runs-on: ubuntu-latest - steps: - - name: PHPUnit Tests - uses: php-actions/phpunit@v2 - with: - bootstrap: vendor/autoload.php - configuration: test/phpunit.xml - args: --coverage-text diff --git a/.gitignore b/.gitignore index 28b7ff542..ba6e91405 100644 --- a/.gitignore +++ b/.gitignore @@ -30,5 +30,11 @@ client/.env api/config.php api/vendor - - +api/tests/.phpunit.result.cache +api/tests/_coverage +api/src/MockUAS.php + +entrypoint.bash +php-fpm.conf +php-fpm.pid +php.ini diff --git a/README.md b/README.md index 9c5aa8fd3..d0164f719 100644 --- a/README.md +++ b/README.md @@ -5,24 +5,43 @@ The client includes some newer components written in Vue.js Read More: https://diamondlightsource.github.io/SynchWeb/ ## Installation -Running SynchWeb requires setting up a Linux, Apache, MariaDB and PHP (LAMP) software stack. If running in production you should configure your Apache and PHP to serve secure pages only. The steps below describe how to build the software so it is ready to deploy onto your target server. - -For development, a simple environment can be setup by using scripts provided [here](https://github.com/DiamondLightSource/synchweb-devel-env). They are not intended for production use but include scripts to automatically build and deploy the software on a local VM. +Running SynchWeb requires setting up a Linux, Apache, MariaDB and PHP (LAMP) software stack. If running in production you should configure your Apache and PHP to serve secure pages only. The steps below describe how to build the software so it is ready to deploy onto your target server. +The [podman](./podman) folder provides support for creating a containerised +production deployment. Full instructions [here](./podman/README.md). + +For development (not production use), a simple environment can be setup by using scripts provided +[here](https://github.com/DiamondLightSource/synchweb-devel-env). Support is provided for both +containerisation and the use of VMs. VS Code provides a good development environment for working +with the SynchWeb codebase. PHP Tools extension provides intellisense, debugging, formatting, +linting and support for unit tests. Vetur and Volar extensions provide support for working with +the Vue.js code. They are not intended for production use but include scripts to automatically + build and deploy the software on a local VM or in a Podman container. ### Requirements -To build SynchWeb on a machine you will need [npm](https://docs.npmjs.com/) and [composer](https://getcomposer.org/) +If not using the Podman containter, to build SynchWeb on a machine you will need the following on the build machine: + +- [npm](https://docs.npmjs.com/) +- [composer](https://getcomposer.org/) +- appropriate version of PHP on the build machine + +If not using the development VMs you will also need an instance of the +ISPyB database - available +[here](https://github.com/DiamondLightSource/ispyb-database). -You will also need php5 on the build machine. +If not already installed, you must install the following packages: -If not using the development VMs you will also need an instance of the ISPyB database [here](https://github.com/DiamondLightSource/ispyb-database) +``` +php php-mysqlnd php-mbstring php-xml php-gd php-fpm php-cli php-xdebug +``` ### Check out the code ```sh $ git clone https://github.com/DiamondLightSource/SynchWeb ``` + ### Customise front end - config.json -An example configuration is provided in client/src/js/config_sample.json -This file should be copied to create a client/src/js/config.json file and edited to customise the application for your site. +An example configuration is provided in `client/src/js/config_sample.json` +This file should be copied to create a `client/src/js/config.json` file and edited to customise the application for your site. | Parameter | Description | | ------ | ------ | @@ -51,26 +70,58 @@ $ npm run build ``` ### Customise back end - config.php -An example configuration is provided in api/config_sample.php +An example configuration is provided in `api/config_sample.php`. This should be copied to +`api/config.php` and updated to include appropriate configuration details. + Main items to change include: - database connection parameters (user, password, host, port) - authentication type (cas, ldap, dummy/no authentication) -### Build backend end +### Build backend ```sh $ cd SynchWeb/api $ composer install ``` -### Developing the client application +Note, the front and backend are built automatically in the Podman deployment. + +### Run backend tests +Tests are available for the PHP code under `api/tests`. To run these, go to the `api` directory and use: + +```sh +$ cd SynchWeb/api +$ ./vendor/bin/phpunit --verbose -c tests/phpunit.xml +``` +Note, a single test can be run by specifying that instead of the `tests` directory. Tests +will also produce a coverage report - this can be disabled by specifying `--no-coverage` when +running the tests. + +### Debugging back end tests +It is possible to debug the php tests. Install xdebug and using an IDE such as VS Code. You +can then start the debugger in the IDE and put break points in the code. Running the tests +(from the command line or within VS Code) will trigger the debugger and execution will be +halted on break points or specified error types. + +### Run front end tests for Vue.js +Testing on the front end is restricted to the newer Vue.js code as it is +anticipated that the older code will eventually be migrated to this form. +To run these tests, + +```sh +$ cd SynchWeb/client +$ npm run test +``` + +## Developing the client application It is possible to run the client code on a local machine and connect to an existing SynchWeb installation on a server. -The steps required are to build the front end code and then run a webpack dev server to host the client code. +The steps required are to build the front end code and then run a webpack dev server to host the client code. Note, this is possible for both the +Podman and VM approach detailed [here](https://github.com/DiamondLightSource/synchweb-devel-env). ```sh $ cd SynchWeb/client $ npm run build:dev -$ npm run serve -- --env.port=8080 --env.proxy.target=http://192.168.33.10 +$ npm run serve -- --env port=8080 --env.proxy.target=http://localhost:8082 ``` -In this example a browser pointed at localhost:8080 will connect to a SynchWeb back end on 192.168.33.10. Don't ignore the middle '--' otherwise the dev server will not receive the arguments! +In this example a browser pointed at localhost:9000 will connect to a SynchWeb back end on localhost:8082. Don't ignore the middle '--' otherwise the dev server will not receive the arguments! The command line options available are described in this table. These override the defaults set in webpack.config.js. @@ -78,14 +129,49 @@ The command line options available are described in this table. These override t | ------ | ------ | | env.port | Webpack dev server port | | env.proxy.target | Full address of the SynchWeb PHP backend server (can include port if required) | -| env.proxy.secure | Flag to set if connecting to an https address for the SynchWeb backend | - - -Acknowledgements +| env.proxy.secure | Flag to set if connecting to an https address for the SynchWeb backend. Setting to `false` can also help with self-signed SSL certs (which may be insecure so should not be used in production). | + +## Continuous Integration +Basic CI is included via the GitHub workflows functionality, defined by +`.github/workflows/ci.yml`. Currently this will run whenever a branch change or +pull request is pushed to `master`, `pre-release` or `release`. The workflow will run two parallel jobs: + +* Checkout the SynchWeb code - for the PHP build + 1. Install the correct version of PHP + 1. Validate the `composer.json` file + 1. Set up a cache for the composer dependencies + 1. Install the required composer dependencies + 1. Run the PHP unit tests - using PHPUnit + 1. Run linting against the PHP code, using PSalm +* Checkout the SynchWeb code - for the JavaScript build + 1. Install npm dependencies (using `ci` mode) + 1. Build the JavaScript bundle + 1. Run Vue unit tests + 1. Run basic JavaScript linting + 1. Run Vue linting + +Note, currently the workflows will not fail if linting errors or warnings are +encountered - this is to enable an initial period of tidying to be enacted. Once +the code is in a suitable state, the rules should be tightened to prevent changes +that introduce new issues. + +## Work in Progress +The codebase is currently subject to some degree of refactoring. The front end is being gradually +migrated away from its Backbone/Marionette origins to use Vue.js instead. Additionally, the +PHP back end is being updated to have a more structured form - breaking down the Page monolith +classes into a more layered architecture - with data layer services under the `Model` folder, and +controller/service classes under the `Controllers` folder. The intention here is to isolate data +access code in a separate layer to allow a more formal API to be identified and to decouple the code +to simplify testing and maintenance. The `Controllers` code currently combines what could be +further split into separate controller and service classes if this was deemed worthwhile (e.g. to +facilitate code reuse). Dependency injection is being introduced (see `index.php`) using the Slim +framework. This could potentially be simplified if common conventions are introduced (e.g. similar +to those used in `Dispatch.php` for setting up the original routes). Once more formal APIs are +identified, it may make sense to introduce proper interfaces to codify these. Swagger-like tools +can then be used to improve documentation and testing of exposed web APIs. + +### Acknowledgements ---------------- If you make use of code from this repository, please reference: Fisher et al., J. Appl. Cryst. (2015). 48, 927-932, doi:10.1107/S1600576715004847 https://journals.iucr.org/j/issues/2015/03/00/fs5101/index.html - - - diff --git a/api/assets/emails/dewar-dispatch.html b/api/assets/emails/dewar-dispatch.html index e01fbf6a8..9fc0c2a2b 100644 --- a/api/assets/emails/dewar-dispatch.html +++ b/api/assets/emails/dewar-dispatch.html @@ -38,7 +38,7 @@ Courier account number: -Courer airway bill number: +Courier air waybill number: Collection date diff --git a/api/assets/emails/dewar-transfer.html b/api/assets/emails/dewar-transfer.html index 7d930256d..099ed9587 100644 --- a/api/assets/emails/dewar-transfer.html +++ b/api/assets/emails/dewar-transfer.html @@ -5,16 +5,7 @@ Dear local contact: , -Your user: requested an internal transfer for his/her dewar(s). Please print this e-mail and affix it on the shipping case and put the dewar for the visit on the rack outside the beamline: "to be Stored at Diamond" by the . - -Local contact for next visit: - - -Dear EHC, - -Please collect the dewar from visit (Dewar Code: ) currently stored on the rack outside and transfer it to the rack in the MX storage room -The dewar should be ready for internal transfer by the - +Your user: from visit requested an internal transfer for his/her dewar(s). Please print this e-mail and affix it on the shipping case. Information for the transfer: @@ -42,14 +33,11 @@ Current dewar location -New Location in storage room - - -Pickup date: - - Local contact email (in case of problems finding the dewar): +Local contact for next visit: + + Comments: diff --git a/api/assets/emails/html/dewar-dispatch.html b/api/assets/emails/html/dewar-dispatch.html index 03c656617..d32821102 100644 --- a/api/assets/emails/html/dewar-dispatch.html +++ b/api/assets/emails/html/dewar-dispatch.html @@ -7,6 +7,21 @@

+ + + + Air Waybill + + + + + Air Waybill + Shipping service could not book the shipment.
+ Goods Handling, please book a shipment for this Dewar using the details below. + + + + Visit @@ -32,7 +47,10 @@

- + Courier Account No. - Airway Bill No. + Air Waybill No. diff --git a/api/assets/emails/html/dewar-transfer.html b/api/assets/emails/html/dewar-transfer.html index be0387bd6..7be5954c1 100644 --- a/api/assets/emails/html/dewar-transfer.html +++ b/api/assets/emails/html/dewar-transfer.html @@ -3,16 +3,9 @@

Local Contact:

-

requested an internal transfer for his/her dewar(s). Please print this e-mail and affix it on the shipping case and put the dewar for the visit on the rack outside the beamline: "to be Stored at Diamond" by the

-

Local contact for next visit:

+

from visit requested an internal transfer for his/her dewar(s). Please print this e-mail and affix it on the shipping case.

-
-

EHCs

-

Please collect the dewar from visit (Dewar Code: ) currently stored on the rack outside and transfer it to the rack in the MX storage room

-

The dewar should be ready for internal transfer by the

-
- @@ -40,11 +33,10 @@

EHCs

- - + + - @@ -61,12 +53,7 @@

EHCs

- - - - - - + diff --git a/api/assets/pdf/shipment_label.php b/api/assets/pdf/shipment_label.php index 48c1aafb5..4fb7a630a 100644 --- a/api/assets/pdf/shipment_label.php +++ b/api/assets/pdf/shipment_label.php @@ -93,18 +93,14 @@
-
-
- -

Frozen samples in Dry-Shipper for experiments at facility

-
-

Not restricted,
As per IATA special provision A152

-

HANDLE WITH CARE

DO NOT DROP

+
+

Cryogenic dry shipper. Not restricted,
ADR A346, IATA A152.

+ 0): ?>

Auto Collect

@@ -119,10 +115,7 @@
-
TO:
-
-

-
+

@@ -188,17 +181,13 @@
-
-
- -

Frozen samples in Dry-Shipper for experiments at DLS

-
-

Not restricted,
As per IATA special provision A152

-

HANDLE WITH CARE

DO NOT DROP

+ +
+

Cryogenic dry shipper. Not restricted,
ADR A346, IATA A152.

@@ -207,16 +196,13 @@
-
TO:
-
-

-
-
-
- Tel:
- Fax:
-

-
+

+
+
+
+ Tel:
+ Fax:
+

diff --git a/api/assets/pdf/visit_report.php b/api/assets/pdf/visit_report.php index 75b3f7fe2..efa817515 100644 --- a/api/assets/pdf/visit_report.php +++ b/api/assets/pdf/visit_report.php @@ -27,7 +27,7 @@

Registered Users

    -
  • +
diff --git a/api/composer.json b/api/composer.json index 9e3bd5369..b2c642c44 100644 --- a/api/composer.json +++ b/api/composer.json @@ -10,26 +10,39 @@ "repositories": [ { "type": "github", - "url": "https://github.com/stufisher/DHL-API.git" + "url": "https://github.com/mattprit/dhl-api.git" } ], "require": { - "alfallouji/dhl_api": "dev-master#8a1699a5", "apereo/phpcas": "1.3.8", "ezyang/htmlpurifier": "4.12.0", "firebase/php-jwt": "2.2.0", "jdorn/sql-formatter": "1.2.9", - "mpdf/mpdf": "6.1.4", - "phpxmlrpc/phpxmlrpc": "3.1.2", + "mpdf/mpdf": "8.1.2", "ralouphie/getallheaders": "2.0.5", - "slim/slim": "2.4.3", + "slim/slim": "2.6.2", "stomp-php/stomp-php": "3.0.6", "symfony/http-foundation": "^2.8", - "symfony/filesystem": "^2.8" + "symfony/filesystem": "^2.8", + "mpdf/qrcode": "^1.2", + "mtcmedia/dhl-api": "dev-master#9b4b6315", + "maennchen/zipstream-php": "2.1.0" }, "autoload": { "psr-4": { - "SynchWeb\\": "src/" + "SynchWeb\\": "src/", + "Tests\\": "tests/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9", + "php-mock/php-mock-phpunit": "^2.6", + "mockery/mockery": "^1.5", + "vimeo/psalm": "^4.27" + }, + "config": { + "platform": { + "php": "7.3" } } -} +} \ No newline at end of file diff --git a/api/config_sample.php b/api/config_sample.php index 0a2fe9d20..e2d3d1b36 100644 --- a/api/config_sample.php +++ b/api/config_sample.php @@ -4,7 +4,6 @@ # - Many of the parameters in this file will in due course move into the # database - # Production / Dev Mode Switch # - Dev mode enabled debugging to stdout in addition to httpd error_log # Values: dev | production @@ -14,32 +13,48 @@ $isb = array('user' => 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb'); $dbtype = 'mysql'; + # Summary Database credentials + ######### DELETE if not using connection. + $summarydbconfig = array('user' => 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb'); + $ifsummary = true; # Encoded JWT key, used to sign and check validaty of jwt tokens # - Create one of these using /api/authenticate/key # This can be changed to invalidate all currently active tokens $jwt_key = ''; - # Auth type - # Can be cas, ldap + # Can be cas, ldap, oidc $authentication_type = 'cas'; - # CAS url (if using cas, assume https) $cas_url = 'cas.server.ac.uk'; # Follow CAS SSO $cas_sso = true; + $sso_url = "sso.server.ac.uk"; + + # OIDC (or OAuth2) client ID and secret. Only useful if authentication_type is set to OIDC + $oidc_client_id = "oidcClientId"; + $oidc_client_secret = "oidcClientSecret"; + # Cookie key used for SSO/cookie based authentication + $cookie_key = "synchweb-auth"; # CAS CA Cert (for SSO) $cacert = '/etc/certs/ca-bundle.crt'; - - # ldap server, used for lookup and authentication (if using) + # ldap server, used for lookup and authentication (if using, set to null if not) # Update the ldap(s) prefix, hostname and search settings as required $ldap_server = 'ldaps://ldap.example.com'; $ldap_search = 'ou=people,dc=example,dc=com'; + # Specify the LDAP server type, can be either + # "openldap" (default) or "activedirectory" + $ldap_server_type = "openldap"; + # If using "activedirectory" then specify the legacy domain name. + # i.e. "MYDOMAIN" rather than "mydomain.com" + # This will be prepended onto the username (e.g. MYDOMAIN\mylogin) + $active_directory_domain = "MYDOMAIN"; + $ldap_use_tls = false; # default - i.e. don't use secured LDAP connection # Upload directory # - used for user image uploads @@ -56,6 +71,9 @@ # Timezone $timezone = 'Europe/London'; + # URL to access the PV archiver + $archive_url = ''; + # Valid Components # Denotes that only staff may create proteins, otherwise they must come from replication # with a valid `externalid`, users may still clone proteins @@ -136,9 +154,10 @@ # - The feedback form uses this address $email_admin = 'webmaster@server.ac.uk'; - # Recepients for dewar Dispatch / Transfers Emails when users request dispatch or tranfser from the shipping page + # Recipients for dewar Dispatch / Transfers Emails when users request dispatch or tranfser from the shipping page $dispatch_email = 'ehc@server.ac.uk, goods@server.ac.uk'; $transfer_email = 'ehc@server.ac.uk'; + $arrival_email = 'ehc@server.ac.uk'; # and for RED experiments, # email will be sent for shipments containing red level samples when "send to facility" is clicked @@ -147,6 +166,13 @@ # and for shipment booked, $shipbooked_email = 'goods@server.ac.uk'; + # dewar back in storage (complete) + $dewar_complete_email = ''; + + # Send a 'visit finished' email when a dewar moves from this beamline to this (regex) location + $dewar_complete_email_locations = array('i03' => '/tray-\w+/', + ); + # Industrial Contacts # - Industrial users get a personalised email with in contact details, # template in assets/emails/dewar-stores-in-in.html @@ -205,6 +231,8 @@ $package_description = 'Dry shipper containing frozen crystals'; $dewar_weight = 18; + # location used by Mpdf to create pdfs - this needs to be created and allow apache to create directories in it + $pdf_tmp_dir = "/tmp"; # DHL API Details $dhl_enable = true; @@ -224,6 +252,15 @@ // Non dom service (eu) $dhl_service_eu = 'U'; + # Shipping service details + $use_shipping_service = null; + $use_shipping_service_incoming_shipments = null; + $shipping_service_api_url = null; + $shipping_service_api_user = null; + $shipping_service_api_password = null; + $shipping_service_app_url = null; + $shipping_service_links_in_emails = null; + # VMXi $vmxi_user = 'vmxi'; @@ -333,5 +370,7 @@ 'i03' => 'BL03I', ); - + # Dials server values + $dials_rest_url = ""; + $dials_rest_jwt = ""; ?> diff --git a/api/docs/definitions/contact-new.yaml b/api/docs/definitions/contact-new.yaml index ed30d5895..c794c5a95 100644 --- a/api/docs/definitions/contact-new.yaml +++ b/api/docs/definitions/contact-new.yaml @@ -2,17 +2,17 @@ type: object properties: CARDNAME: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: Lab Card Name example: Card Name GIVENNAME: type: string - pattern: '([\w-])+' + pattern: '([\w\-])+' description: First Name example: John FAMILYNAME: type: string - pattern: '([\w-])+' + pattern: '([\w\-])+' description: Family Name example: Smith PHONENUMBER: @@ -25,40 +25,40 @@ properties: example: a@b.com LABNAME: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: Lab Name example: Lab Name ADDRESS: type: string - pattern: '([\w\s-\n])+' + pattern: '([\w\s\-\n])+' description: Address example: 123 Synchrotron Road\nSynchrotron CITY: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: City example: City POSTCODE: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: Post Code example: 12345 COUNTRY: type: string - pattern: ([\w\s-,\(\)\'])+ + pattern: ([\w\s\-,\(\)\'])+ description: Country example: United States COURIERACCOUNT: type: string - pattern: '([\w-])+' + pattern: '([\w\-])+' description: Courier Account Number BILLINGREFERENCE: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: Billing Reference DEFAULTCOURRIERCOMPANY: type: string - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' description: Courier Company DEWARAVGCUSTOMSVALUE: type: string diff --git a/api/docs/definitions/container-new.yaml b/api/docs/definitions/container-new.yaml index 5b4093f89..b28c358fd 100644 --- a/api/docs/definitions/container-new.yaml +++ b/api/docs/definitions/container-new.yaml @@ -4,7 +4,7 @@ properties: type: string description: Container name example: Container1 - pattern: ([\w-])+ + pattern: ([\w\-])+ CONTAINERTYPE: type: string description: Type of container @@ -47,7 +47,7 @@ properties: BARCODE: type: string description: Container barcode - pattern: ([\w-])+ + pattern: ([\w\-])+ example: vmxi-124242342-2342 EXPERIMENTTYPE: type: string @@ -60,7 +60,7 @@ properties: STORAGETEMPERATURE: type: string description: Requested storage temperature - pattern: '[\w-]+' + pattern: '[\w\-]+' example: 24 required: diff --git a/api/docs/definitions/dewar-new.yaml b/api/docs/definitions/dewar-new.yaml index df7b6b9ea..37a40dd86 100644 --- a/api/docs/definitions/dewar-new.yaml +++ b/api/docs/definitions/dewar-new.yaml @@ -9,7 +9,7 @@ properties: type: string description: Dewar name example: DLS-MX-1234 - pattern: '([\w-])+' + pattern: '([\w\-])+' TRACKINGNUMBERTOSYNCHROTRON: type: string description: Tracking number on route to facility @@ -24,7 +24,7 @@ properties: type: string description: Unique identifier of this dewar (barcode at DLS) example: DLS-MX-1234 - pattern: '([\w-])+' + pattern: '([\w\-])+' FIRSTEXPERIMENTID: type: string description: First visit id diff --git a/api/docs/definitions/dewar.yaml b/api/docs/definitions/dewar.yaml index 18be0946e..5f0427df7 100644 --- a/api/docs/definitions/dewar.yaml +++ b/api/docs/definitions/dewar.yaml @@ -34,7 +34,7 @@ allOf: type: string description: Dewar shipment barcode example: cm14451-2-i03-0008624 - pattern: '([\w-])+' + pattern: '([\w\-])+' STORAGELOCATION: type: string description: Current storage location (unused?) diff --git a/api/docs/definitions/protein-new.yaml b/api/docs/definitions/protein-new.yaml index a3918423d..b5b1e2cbe 100644 --- a/api/docs/definitions/protein-new.yaml +++ b/api/docs/definitions/protein-new.yaml @@ -2,13 +2,13 @@ type: object properties: ACRONYM: type: string - pattern: ([\w-])+ + pattern: ([\w\-])+ description: Protein acronym example: prot1 NAME: type: string - pattern: ([\w-])+ + pattern: ([\w\-])+ description: Protein acronym example: Protein one SEQUENCE: diff --git a/api/docs/definitions/sample-new.yaml b/api/docs/definitions/sample-new.yaml index 09738ccc2..6edeb5160 100644 --- a/api/docs/definitions/sample-new.yaml +++ b/api/docs/definitions/sample-new.yaml @@ -22,7 +22,7 @@ properties: example: '1' NAME: type: string - pattern: '[\w\s-()]+' + pattern: '[\w\s\-()]+' description: Sample name example: xtal4 diff --git a/api/docs/definitions/shipment-new.yaml b/api/docs/definitions/shipment-new.yaml index 1c3921d70..e68d1a3c8 100644 --- a/api/docs/definitions/shipment-new.yaml +++ b/api/docs/definitions/shipment-new.yaml @@ -4,7 +4,7 @@ properties: type: string description: Shipment Name example: ship1 - pattern: '([\w\s-])+' + pattern: '([\w\s\-])+' SENDINGLABCONTACTID: type: string description: Lab Contact id sending the shipment @@ -29,12 +29,12 @@ properties: type: string description: Courier Name example: dhl - pattern: '[\s|\w|-]+' + pattern: '[\s|\w|\-]+' DELIVERYAGENT_AGENTCODE: type: string description: Courier Acount Number example: 124 - pattern: '[\w-]+' + pattern: '[\w\-]+' DELIVERYAGENT_SHIPPINGDATE: type: string description: Date to ship @@ -54,7 +54,7 @@ properties: type: string description: Pickup location of shipment (for DHL pickup) example: stores out - pattern: '[\s|\w|-]+' + pattern: '[\s|\w|\-]+' READYBYTIME: type: string description: Shipment ready by time (for DHL pickup) diff --git a/api/docs/parameters/index.yaml b/api/docs/parameters/index.yaml index b5889c603..834af2cc0 100644 --- a/api/docs/parameters/index.yaml +++ b/api/docs/parameters/index.yaml @@ -2,7 +2,7 @@ SearchParam: name: s in: query type: string - pattern: '[\w\s-]+' + pattern: '[\w\s\-]+' description: Search string PageNumberParam: @@ -63,7 +63,7 @@ BLParam: name: bl type: string in: query - pattern: '[\w-]+' + pattern: '[\w\-]+' description: Beamline diff --git a/api/formulatrix/WSPlate.wsdl b/api/formulatrix/WSPlate.wsdl deleted file mode 100644 index 9fc8e8ea3..000000000 --- a/api/formulatrix/WSPlate.wsdl +++ /dev/null @@ -1,1408 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/api/formulatrix/bindings/ArrayCapturePoint.php b/api/formulatrix/bindings/ArrayCapturePoint.php deleted file mode 100644 index 1db6e5017..000000000 --- a/api/formulatrix/bindings/ArrayCapturePoint.php +++ /dev/null @@ -1,14 +0,0 @@ - 'uk\ac\ox\oppf\www\WSPlate\Point', - 'Size' => 'uk\ac\ox\oppf\www\WSPlate\Size', - 'PlateInfo' => 'uk\ac\ox\oppf\www\WSPlate\PlateInfo', - 'PlateType' => 'uk\ac\ox\oppf\www\WSPlate\PlateType', - 'Property' => 'uk\ac\ox\oppf\www\WSPlate\Property', - 'Robot' => 'uk\ac\ox\oppf\www\WSPlate\Robot', - 'ImagingTask' => 'uk\ac\ox\oppf\www\WSPlate\ImagingTask', - 'CapturePoint' => 'uk\ac\ox\oppf\www\WSPlate\CapturePoint', - 'CapturePointList' => 'uk\ac\ox\oppf\www\WSPlate\CapturePointList', - 'CaptureProfile' => 'uk\ac\ox\oppf\www\WSPlate\CaptureProfile', - 'GetFirstDropReturn' => 'uk\ac\ox\oppf\www\WSPlate\GetFirstDropReturn', - 'CaptureInfo' => 'uk\ac\ox\oppf\www\WSPlate\CaptureInfo', - 'FocalPoint' => 'uk\ac\ox\oppf\www\WSPlate\FocalPoint', - 'ImageInfo' => 'uk\ac\ox\oppf\www\WSPlate\ImageInfo', - 'ProcessingInfo' => 'uk\ac\ox\oppf\www\WSPlate\ProcessingInfo', - 'UploadImage' => 'uk\ac\ox\oppf\www\WSPlate\UploadImage', - 'UploadImageResponse' => 'uk\ac\ox\oppf\www\WSPlate\UploadImageResponse', - 'Sample' => 'uk\ac\ox\oppf\www\WSPlate\Sample', - 'Project' => 'uk\ac\ox\oppf\www\WSPlate\Project', - 'WSPlateError' => 'uk\ac\ox\oppf\www\WSPlate\WSPlateError', - 'ArrayPlateType' => 'uk\ac\ox\oppf\www\WSPlate\ArrayPlateType', - 'ArrayProperty' => 'uk\ac\ox\oppf\www\WSPlate\ArrayProperty', - 'ArrayImagingTask' => 'uk\ac\ox\oppf\www\WSPlate\ArrayImagingTask', - 'ArrayCapturePoint' => 'uk\ac\ox\oppf\www\WSPlate\ArrayCapturePoint', - 'ArrayCaptureProfile' => 'uk\ac\ox\oppf\www\WSPlate\ArrayCaptureProfile', - 'ArrayFocalPoint' => 'uk\ac\ox\oppf\www\WSPlate\ArrayFocalPoint', - 'ArrayUploadImage' => 'uk\ac\ox\oppf\www\WSPlate\ArrayUploadImage', - 'ArrayUploadImageResponse' => 'uk\ac\ox\oppf\www\WSPlate\ArrayUploadImageResponse', - 'ArraySample' => 'uk\ac\ox\oppf\www\WSPlate\ArraySample', - 'ArrayProject' => 'uk\ac\ox\oppf\www\WSPlate\ArrayProject', - 'getPlateID' => 'uk\ac\ox\oppf\www\WSPlate\getPlateID', - 'getPlateIDResponse' => 'uk\ac\ox\oppf\www\WSPlate\getPlateIDResponse', - 'getPlateInfo' => 'uk\ac\ox\oppf\www\WSPlate\getPlateInfo', - 'getPlateInfoResponse' => 'uk\ac\ox\oppf\www\WSPlate\getPlateInfoResponse', - 'getPlateType' => 'uk\ac\ox\oppf\www\WSPlate\getPlateType', - 'getPlateTypeResponse' => 'uk\ac\ox\oppf\www\WSPlate\getPlateTypeResponse', - 'getPlateTypes' => 'uk\ac\ox\oppf\www\WSPlate\getPlateTypes', - 'getPlateTypesResponse' => 'uk\ac\ox\oppf\www\WSPlate\getPlateTypesResponse', - 'getImagingTasks' => 'uk\ac\ox\oppf\www\WSPlate\getImagingTasks', - 'getImagingTasksResponse' => 'uk\ac\ox\oppf\www\WSPlate\getImagingTasksResponse', - 'supportsPriority' => 'uk\ac\ox\oppf\www\WSPlate\supportsPriority', - 'supportsPriorityResponse' => 'uk\ac\ox\oppf\www\WSPlate\supportsPriorityResponse', - 'updatedPriority' => 'uk\ac\ox\oppf\www\WSPlate\updatedPriority', - 'updatedPriorityResponse' => 'uk\ac\ox\oppf\www\WSPlate\updatedPriorityResponse', - 'imagingPlate' => 'uk\ac\ox\oppf\www\WSPlate\imagingPlate', - 'imagingPlateResponse' => 'uk\ac\ox\oppf\www\WSPlate\imagingPlateResponse', - 'imagedPlate' => 'uk\ac\ox\oppf\www\WSPlate\imagedPlate', - 'imagedPlateResponse' => 'uk\ac\ox\oppf\www\WSPlate\imagedPlateResponse', - 'skippedImaging' => 'uk\ac\ox\oppf\www\WSPlate\skippedImaging', - 'skippedImagingResponse' => 'uk\ac\ox\oppf\www\WSPlate\skippedImagingResponse', - 'getCapturePoints' => 'uk\ac\ox\oppf\www\WSPlate\getCapturePoints', - 'getCapturePointsResponse' => 'uk\ac\ox\oppf\www\WSPlate\getCapturePointsResponse', - 'getDefaultCaptureProfile' => 'uk\ac\ox\oppf\www\WSPlate\getDefaultCaptureProfile', - 'getDefaultCaptureProfileResponse' => 'uk\ac\ox\oppf\www\WSPlate\getDefaultCaptureProfileResponse', - 'getFirstDrop' => 'uk\ac\ox\oppf\www\WSPlate\getFirstDrop', - 'getFirstDropResponse' => 'uk\ac\ox\oppf\www\WSPlate\getFirstDropResponse', - 'getImageProcessor' => 'uk\ac\ox\oppf\www\WSPlate\getImageProcessor', - 'getImageProcessorResponse' => 'uk\ac\ox\oppf\www\WSPlate\getImageProcessorResponse', - 'uploadImages' => 'uk\ac\ox\oppf\www\WSPlate\uploadImages', - 'uploadImagesResponse' => 'uk\ac\ox\oppf\www\WSPlate\uploadImagesResponse', - 'listSamples' => 'uk\ac\ox\oppf\www\WSPlate\listSamples', - 'listSamplesResponse' => 'uk\ac\ox\oppf\www\WSPlate\listSamplesResponse', - 'listProjects' => 'uk\ac\ox\oppf\www\WSPlate\listProjects', - 'listProjectsResponse' => 'uk\ac\ox\oppf\www\WSPlate\listProjectsResponse', - 'GetPlateIDFault' => 'uk\ac\ox\oppf\www\WSPlate\GetPlateIDFault', - 'GetPlateInfoFault' => 'uk\ac\ox\oppf\www\WSPlate\GetPlateInfoFault', - 'GetPlateTypeFault' => 'uk\ac\ox\oppf\www\WSPlate\GetPlateTypeFault', - 'GetPlateTypesFault' => 'uk\ac\ox\oppf\www\WSPlate\GetPlateTypesFault', - 'GetImagingTasksFault' => 'uk\ac\ox\oppf\www\WSPlate\GetImagingTasksFault', - 'SupportsPriorityFault' => 'uk\ac\ox\oppf\www\WSPlate\SupportsPriorityFault', - 'UpdatedPriorityFault' => 'uk\ac\ox\oppf\www\WSPlate\UpdatedPriorityFault', - 'ImagingPlateFault' => 'uk\ac\ox\oppf\www\WSPlate\ImagingPlateFault', - 'ImagedPlateFault' => 'uk\ac\ox\oppf\www\WSPlate\ImagedPlateFault', - 'SkippedImagingFault' => 'uk\ac\ox\oppf\www\WSPlate\SkippedImagingFault', - 'GetCapturePointsFault' => 'uk\ac\ox\oppf\www\WSPlate\GetCapturePointsFault', - 'GetDefaultCaptureProfileFault' => 'uk\ac\ox\oppf\www\WSPlate\GetDefaultCaptureProfileFault', - 'GetFirstDropFault' => 'uk\ac\ox\oppf\www\WSPlate\GetFirstDropFault', - 'GetImageProcessorFault' => 'uk\ac\ox\oppf\www\WSPlate\GetImageProcessorFault', - 'UploadImagesFault' => 'uk\ac\ox\oppf\www\WSPlate\UploadImagesFault', - 'ListSamplesFault' => 'uk\ac\ox\oppf\www\WSPlate\ListSamplesFault', - 'ListProjectsFault' => 'uk\ac\ox\oppf\www\WSPlate\ListProjectsFault', - 'CanListSamplesFault' => 'uk\ac\ox\oppf\www\WSPlate\CanListSamplesFault'); - - /** - * @param array $options A array of config values - * @param string $wsdl The wsdl file to use - * @access public - */ - public function __construct(array $options = array(), $wsdl = 'WSPlate.wsdl') - { - foreach (self::$classmap as $key => $value) { - if (!isset($options['classmap'][$key])) { - $options['classmap'][$key] = $value; - } - } - - parent::__construct($wsdl, $options); - } - - /** - * @param getPlateID $part1 - * @access public - * @return getPlateIDResponse - */ - public function getPlateID(getPlateID $part1) - { - return $this->__soapCall('getPlateID', array($part1)); - } - - /** - * @param getCapturePoints $part1 - * @access public - * @return getCapturePointsResponse - */ - public function getCapturePoints(getCapturePoints $part1) - { - return $this->__soapCall('getCapturePoints', array($part1)); - } - - /** - * @param getDefaultCaptureProfile $part1 - * @access public - * @return getDefaultCaptureProfileResponse - */ - public function getDefaultCaptureProfile(getDefaultCaptureProfile $part1) - { - return $this->__soapCall('getDefaultCaptureProfile', array($part1)); - } - - /** - * @param updatedPriority $part1 - * @access public - * @return updatedPriorityResponse - */ - public function updatedPriority(updatedPriority $part1) - { - return $this->__soapCall('updatedPriority', array($part1)); - } - - /** - * @param getPlateInfo $part1 - * @access public - * @return getPlateInfoResponse - */ - public function getPlateInfo(getPlateInfo $part1) - { - return $this->__soapCall('getPlateInfo', array($part1)); - } - - /** - * @param getPlateTypes $part1 - * @access public - * @return getPlateTypesResponse - */ - public function getPlateTypes(getPlateTypes $part1) - { - return $this->__soapCall('getPlateTypes', array($part1)); - } - - /** - * @param imagingPlate $part1 - * @access public - * @return imagingPlateResponse - */ - public function imagingPlate(imagingPlate $part1) - { - return $this->__soapCall('imagingPlate', array($part1)); - } - - /** - * @param imagedPlate $part1 - * @access public - * @return imagedPlateResponse - */ - public function imagedPlate(imagedPlate $part1) - { - return $this->__soapCall('imagedPlate', array($part1)); - } - - /** - * @param getImagingTasks $part1 - * @access public - * @return getImagingTasksResponse - */ - public function getImagingTasks(getImagingTasks $part1) - { - return $this->__soapCall('getImagingTasks', array($part1)); - } - - /** - * @param getImageProcessor $part1 - * @access public - * @return getImageProcessorResponse - */ - public function getImageProcessor(getImageProcessor $part1) - { - return $this->__soapCall('getImageProcessor', array($part1)); - } - - /** - * @param getPlateType $part1 - * @access public - * @return getPlateTypeResponse - */ - public function getPlateType(getPlateType $part1) - { - return $this->__soapCall('getPlateType', array($part1)); - } - - /** - * @param supportsPriority $part1 - * @access public - * @return supportsPriorityResponse - */ - public function supportsPriority(supportsPriority $part1) - { - return $this->__soapCall('supportsPriority', array($part1)); - } - - /** - * @param getFirstDrop $part1 - * @access public - * @return getFirstDropResponse - */ - public function getFirstDrop(getFirstDrop $part1) - { - return $this->__soapCall('getFirstDrop', array($part1)); - } - - /** - * @param skippedImaging $part1 - * @access public - * @return skippedImagingResponse - */ - public function skippedImaging(skippedImaging $part1) - { - return $this->__soapCall('skippedImaging', array($part1)); - } - - /** - * @param uploadImages $part1 - * @access public - * @return uploadImagesResponse - */ - public function uploadImages(uploadImages $part1) - { - return $this->__soapCall('uploadImages', array($part1)); - } - - /** - * @param listSamples $part1 - * @access public - * @return listSamplesResponse - */ - public function listSamples(listSamples $part1) - { - return $this->__soapCall('listSamples', array($part1)); - } - - /** - * @param listProjects $part1 - * @access public - * @return listProjectsResponse - */ - public function listProjects(listProjects $part1) - { - return $this->__soapCall('listProjects', array($part1)); - } - - /** - * @access public - * @return boolean - */ - public function canListSamples() - { - return $this->__soapCall('canListSamples', array()); - } - -} diff --git a/api/formulatrix/bindings/WSPlateError.php b/api/formulatrix/bindings/WSPlateError.php deleted file mode 100644 index 847c0dd9b..000000000 --- a/api/formulatrix/bindings/WSPlateError.php +++ /dev/null @@ -1,26 +0,0 @@ - '2 Drop Greiner CrystalQuick X', - 'MRCMaxi' => 'Film - Batch', - 'MitagenInSitu' => '2 Drop MiTgen in-situ1' - ); - \ No newline at end of file diff --git a/api/formulatrix/index.php b/api/formulatrix/index.php deleted file mode 100644 index eb883d372..000000000 --- a/api/formulatrix/index.php +++ /dev/null @@ -1,509 +0,0 @@ - - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* SynchWeb implementation of a sufficient subset of WSPlate.wsdl to satisfy - * RockImager, RockImagerProcessor and ImageUploader. - * stuart.fisher@diamond.ac.uk - * - * Jon Diprose, 2015/10/07 - * Stu Fisher, 2016/03/15 - Initial Implementation - */ - -date_default_timezone_set('Europe/London'); - -// Return if theres no config -if (!file_exists('config.php')) exit; -require_once('config.php'); - -/******************************************************************* -************************* ACCESS CONTROL *************************** -*******************************************************************/ - -if ( - ( ! isset( $_SERVER['PHP_AUTH_USER'] ) ) || - ( ! isset( $_SERVER['PHP_AUTH_PW'] ) ) || - ( $username != $_SERVER['PHP_AUTH_USER'] ) || - ( $password != $_SERVER['PHP_AUTH_PW'] ) -) { - header('WWW-Authenticate: Basic realm="WSPlate"'); - header('HTTP/1.1 401 Unauthorized'); - echo 'Unauthorized'; - exit; -} - -/******************************************************************* -*********************** END ACCESS CONTROL ************************* -*******************************************************************/ - - - -/******************************************************************* -*********************** Load Synchweb Funcs ************************ -*******************************************************************/ - -include_once('../includes/pages/class.imaging.shared.php'); -$imaging = new ImagingShared(); - - -$states = array( - 1 => 'Not completed', - 'Skipped', - 'Pending', - 'Queued', - 'Imaging', - 'Completed', - 'Cancelled', -); - -/******************************************************************* -********************* AUTOLOAD XSD BINDINGS ************************ -*******************************************************************/ - -// Autoload the xsd binding classes -function __autoload( $class ) { - if ( strpos( $class, '\\' ) !== FALSE ) { - $parts = explode( '\\', $class ); - $path = 'bindings/' . end( $parts ) . '.php'; - if ( file_exists( $path ) ) { - require_once( $path ); - return; - } - } - trigger_error( 'autoload failed for ' . $class, E_USER_WARNING ); -} - -/******************************************************************* -******************* END AUTOLOAD XSD BINDINGS ********************** -*******************************************************************/ - -/******************************************************************* -****************** IMPLEMENTED WSDL OPERATIONS ********************* -*******************************************************************/ - -// SynchWeb getPlateInfo implementation -function getPlateInfo($getPlateInfo) { - global $imaging, $plate_types; - - // Attempt to extract something useful for plate number - // - note it must fit in an int - // Stu - Not sure what this is used for? - $plateNumber = 0; - $matches = array(); - if (preg_match('#(\d{1,8})$#', $getPlateInfo->plateID, $matches)) { - $plateNumber = $matches[1]; - } - - // Get plateinfo from SynchWeb - $info = $imaging->_get_plate_info(array('BARCODE' => $getPlateInfo->plateID, 'SERIAL' => $getPlateInfo->robot->name)); - - $plateInfo = new uk\ac\ox\oppf\www\WSPlate\PlateInfo(); - $plateInfo->dateDispensed = date("c", strtotime($info['BLTIMESTAMP'])); - $plateInfo->experimentName = $info['SHIPMENT'].' - '.$info['DEWAR']; - $plateInfo->plateNumber = $plateNumber; - $plateInfo->projectName = $info['PROP']; - - // This MUST match the rockimager plate definitions or we'll destroy all plate training - // config.php has a mapping between internal and ispyb plate names - if (!array_key_exists($info['CONTAINERTYPE'], $plate_types)) return; - $plateInfo->plateTypeID = $plate_types[$info['CONTAINERTYPE']]; - - $plateInfo->userEmail = $info['EMAILADDRESS']; - $plateInfo->userName = $info['LOGIN']; - - // error_log(print_r($plateInfo, true)); - - $response = new uk\ac\ox\oppf\www\WSPlate\getPlateInfoResponse(); - $response->getPlateInfoReturn = $plateInfo; - - return $response; -} - -// SynchWeb getImagingTasks implementation -function getImagingTasks($getImagingTasks) { - global $imaging, $states; - - /* - * States: - * 1 - NotCompleted - * 2 - Skipped - * 3 - Pending - not valid server-side - * 4 - Queued - not valid server-side - * 5 - Imaging - not valid server-side - * 6 - Completed - * 7 - Cancelled - not valid server-side - */ - - $arrayImagingTask = new uk\ac\ox\oppf\www\WSPlate\ArrayImagingTask(); - $arrayImagingTask->item = array(); - - $inspections = $imaging->_get_plate_inspections(array('BARCODE' => $getImagingTasks->plateID, 'ALL' => 1)); - foreach ($inspections as $i) { - $imagingTask = new uk\ac\ox\oppf\www\WSPlate\ImagingTask(); - - $dateToImage = strtotime($i['DATETOIMAGE']); - $imagingTask->dateToImage = date('c', $dateToImage); - $imagingTask->dateImaged = $dateToImage >= time() ? null : ($i['DATEIMAGED'] ? date('c', $dateToImage) : null); - $imagingTask->state = array_search($i['STATE'], $states); - $imagingTask->priority = $i['PRIORITY']; - $imagingTask->inQueue = 1 === $imagingTask->state; - - $arrayImagingTask->item[] = $imagingTask; - } - - $response = new uk\ac\ox\oppf\www\WSPlate\getImagingTasksResponse(); - $response->wrapper = $arrayImagingTask; - - return $response; - -} - -// SynchWeb imagingPlate implementation -function imagingPlate($imagingPlate) { - global $imaging, $states; - // state = 5 - - // SoapServer DateTime handling bug - $dateToImage = date_create_from_format(DATE_ATOM, preg_replace('/\.\d+(\w)$/', '$1', $imagingPlate->dateToImage)); - - // This has come from ISPyB - if ($imagingPlate->scheduled) { - // This is totally nuts, we now have to magically retrive the containerinspectionid - // based on the timestamp and barcode, we could have passed a unique id from - // getImagingTasks which RockImager could internally use. Sigh :( - $inspectionid = $imaging->_update_inspection(array( - 'DATETOIMAGE' => $dateToImage->format('d-m-Y H:i'), - 'BARCODE' => $imagingPlate->plateID, - 'VALUES' => array( - 'STATE' => $states[5], - 'BLTIMESTAMP' => date('d-m-Y H:i') - ) - )); - - // The first task of the schedule if scheduled when the container is originally - // created so is always overdue by the time it gets to site. - - // If this is the first imaging for this plate, now schedule the rest of the tasks - // from this current timestamp (what we'll define as zero) - $imaging->_check_schedule_from_zero(array('BARCODE' => $imagingPlate->plateID)); - - - // This one is a manual inspection from RockImager - } else { - $container = $imaging->_get_plate_info(array('BARCODE' => $imagingPlate->plateID)); - - $args = array( - 'CONTAINERID' => $container['CONTAINERID'], - 'INSPECTIONTYPEID' => 1, // configure me - 'DATETOIMAGE' => $dateToImage->format('d-m-Y H:i'), - 'BARCODE' => $imagingPlate->plateID, - 'MANUAL' => 1, - 'STATE' => $states[5], - 'BLTIMESTAMP' => date('d-m-Y H:i') - ); - - if ($container['TEMPERATURE']) $args['TEMPERATURE'] = $container['TEMPERATURE']; - if ($container['IMAGERID']) $args['IMAGERID'] = $container['IMAGERID']; - - $inspectionid = $imaging->_do_insert_inspection($args); - } - - - $response = new uk\ac\ox\oppf\www\WSPlate\imagingPlateResponse(); - // sigh - $response->imagingPlateReturn = $inspectionid . date('-Ymd-His'); - - return $response; - -} - -// SynchWeb imagedPlate implementation -// ... Sanity ... -function imagedPlate($imagedPlate) { - global $imaging, $states; - - // so close to sanity... - $id = preg_replace('/\-.*/', '', $imagedPlate->imagingID); - - // Mark imaging session $imagedPlate->imagingID as completed - // - state 6 - $inspection = $imaging->_do_update_inspection(array( - 'CONTAINERINSPECTIONID' => $id, - 'VALUES' => array( - 'STATE' => $states[6], - 'COMPLETEDTIMESTAMP' => date('d-m-Y H:i') - ) - )); - - $imaging->_email_status_update($id); - - $response = new uk\ac\ox\oppf\www\WSPlate\imagedPlateResponse(); - $response->imagedPlateReturn = true; - - return $response; - -} - -// SynchWeb skippedImaging implementation -function skippedImaging($skippedImaging) { - global $imaging, $states; - - // Mark imaging session $skippedImaging->dateToImage for - // $skippedImaging->plateID as skipped - // - state 2 - $dateToImage = date_create_from_format(DATE_ATOM, $skippedImaging->dateToImage); - $imaging->_update_inspection(array( - 'DATETOIMAGE' => $dateToImage->format('d-m-Y H:i'), - 'BARCODE' => $skippedImaging->plateID, - 'VALUES' => array( - 'STATE' => $states[2], - ) - )); - - $response = new uk\ac\ox\oppf\www\WSPlate\skippedImagingResponse(); - $response->skippedImagingReturn = true; - - return $response; - -} - -// SynchWeb updatedPriority implementation -function updatedPriority($updatedPriority) { - global $imaging; - - // Mark imaging session $updatedPriority->dateToImage for - // $updatedPriority->plateID as having priority - // $updatedPriority->priority - $dateToImage = date_create_from_format(DATE_ATOM, $updatedPriority->dateToImage); - $imaging->_update_inspection(array( - 'DATETOIMAGE' => $dateToImage->format('d-m-Y H:i'), - 'BARCODE' => $updatedPriority->plateID, - 'VALUES' => array( - 'PRIORITY' => $updatedPriority->priority - ) - )); - - $response = new uk\ac\ox\oppf\www\WSPlate\updatedPriorityResponse(); - $response->updatedPriorityReturn = true; - - return $response; - -} - -// SynchWeb uploadImages implementation -function uploadImages($uploadImages) { - - $arrayUploadImage = $uploadImages->wrapper; - - // From here we could directly upload the images, but i guess we might - // timeout the request - - // Client gets informed of per-image success/failure - $arrayUploadImageResponse = new uk\ac\ox\oppf\www\WSPlate\ArrayUploadImageResponse(); - - foreach ($arrayUploadImage->item as $item) { - /* Update records to add the image details using: - * $item->robot - * $item->imagingID - * $item->plateID - * $item->well - * $item->url - at which this image is already available - * $item->image->height, $item->image->width - image size in pxiels - * $item->pixel->height, $item->pixel->width - pixel size in microns - * $item->colourDepth - * $item->dateImaged - * $item->type - composite, slice or zoomed - */ - - // Populate an UploadImageResponse for this image so the client - // knows what happened - $uploadImageResponse = new uk\ac\ox\oppf\www\WSPlate\UploadImageResponse(); - $uploadImageResponse->ok = true; - $uploadImageResponse->url = $item->url; - $uploadImageResponse->reason = null; - - $arrayUploadImageResponse->item[] = $uploadImageResponse; - } - - $uploadImagesResponse = new uk\ac\ox\oppf\www\WSPlate\uploadImagesResponse(); - $uploadImagesResponse->wrapper = $arrayUploadImageResponse; - - return $uploadImagesResponse; - -} - -/******************************************************************* -**************** END IMPLEMENTED WSDL OPERATIONS ******************* -*******************************************************************/ - -/******************************************************************* -*********************** SOAPSERVER SETUP ************************** -*******************************************************************/ - -$server = new SoapServer( - "WSPlate.wsdl", - array( - 'cache_wsdl' => WSDL_CACHE_MEMORY, - 'classmap' => array( - 'ArrayCapturePoint' => "uk\ac\ox\oppf\www\WSPlate\ArrayCapturePoint", - 'ArrayCaptureProfile' => "uk\ac\ox\oppf\www\WSPlate\ArrayCaptureProfile", - 'ArrayFocalPoint' => "uk\ac\ox\oppf\www\WSPlate\ArrayFocalPoint", - 'ArrayImagingTask' => "uk\ac\ox\oppf\www\WSPlate\ArrayImagingTask", - 'ArrayPlateType' => "uk\ac\ox\oppf\www\WSPlate\ArrayPlateType", - 'ArrayProject' => "uk\ac\ox\oppf\www\WSPlate\ArrayProject", - 'ArrayProperty' => "uk\ac\ox\oppf\www\WSPlate\ArrayProperty", - 'ArraySample' => "uk\ac\ox\oppf\www\WSPlate\ArraySample", - 'ArrayUploadImage' => "uk\ac\ox\oppf\www\WSPlate\ArrayUploadImage", - 'ArrayUploadImageResponse' => "uk\ac\ox\oppf\www\WSPlate\ArrayUploadImageResponse", - 'CanListSamplesFault' => "uk\ac\ox\oppf\www\WSPlate\CanListSamplesFault", - 'CanListSamplesResponse' => "uk\ac\ox\oppf\www\WSPlate\CanListSamplesResponse", - 'CaptureInfo' => "uk\ac\ox\oppf\www\WSPlate\CaptureInfo", - 'CapturePointList' => "uk\ac\ox\oppf\www\WSPlate\CapturePointList", - 'CapturePoint' => "uk\ac\ox\oppf\www\WSPlate\CapturePoint", - 'CaptureProfile' => "uk\ac\ox\oppf\www\WSPlate\CaptureProfile", - 'FocalPoint' => "uk\ac\ox\oppf\www\WSPlate\FocalPoint", - 'GetCapturePointsFault' => "uk\ac\ox\oppf\www\WSPlate\GetCapturePointsFault", - 'getCapturePoints' => "uk\ac\ox\oppf\www\WSPlate\getCapturePoints", - 'getCapturePointsResponse' => "uk\ac\ox\oppf\www\WSPlate\getCapturePointsResponse", - 'GetDefaultCaptureProfileFault' => "uk\ac\ox\oppf\www\WSPlate\GetDefaultCaptureProfileFault", - 'getDefaultCaptureProfile' => "uk\ac\ox\oppf\www\WSPlate\getDefaultCaptureProfile", - 'getDefaultCaptureProfileResponse' => "uk\ac\ox\oppf\www\WSPlate\getDefaultCaptureProfileResponse", - 'GetFirstDropFault' => "uk\ac\ox\oppf\www\WSPlate\GetFirstDropFault", - 'getFirstDrop' => "uk\ac\ox\oppf\www\WSPlate\getFirstDrop", - 'getFirstDropResponse' => "uk\ac\ox\oppf\www\WSPlate\getFirstDropResponse", - 'GetFirstDropReturn' => "uk\ac\ox\oppf\www\WSPlate\GetFirstDropReturn", - 'GetImageProcessorFault' => "uk\ac\ox\oppf\www\WSPlate\GetImageProcessorFault", - 'getImageProcessor' => "uk\ac\ox\oppf\www\WSPlate\getImageProcessor", - 'getImageProcessorResponse' => "uk\ac\ox\oppf\www\WSPlate\getImageProcessorResponse", - 'GetImagingTasksFault' => "uk\ac\ox\oppf\www\WSPlate\GetImagingTasksFault", - 'getImagingTasks' => "uk\ac\ox\oppf\www\WSPlate\getImagingTasks", - 'getImagingTasksResponse' => "uk\ac\ox\oppf\www\WSPlate\getImagingTasksResponse", - 'GetPlateIDFault' => "uk\ac\ox\oppf\www\WSPlate\GetPlateIDFault", - 'getPlateID' => "uk\ac\ox\oppf\www\WSPlate\getPlateID", - 'getPlateIDResponse' => "uk\ac\ox\oppf\www\WSPlate\getPlateIDResponse", - 'GetPlateInfoFault' => "uk\ac\ox\oppf\www\WSPlate\GetPlateInfoFault", - 'getPlateInfo' => "uk\ac\ox\oppf\www\WSPlate\getPlateInfo", - 'getPlateInfoResponse' => "uk\ac\ox\oppf\www\WSPlate\getPlateInfoResponse", - 'GetPlateTypeFault' => "uk\ac\ox\oppf\www\WSPlate\GetPlateTypeFault", - 'getPlateType' => "uk\ac\ox\oppf\www\WSPlate\getPlateType", - 'getPlateTypeResponse' => "uk\ac\ox\oppf\www\WSPlate\getPlateTypeResponse", - 'GetPlateTypesFault' => "uk\ac\ox\oppf\www\WSPlate\GetPlateTypesFault", - 'getPlateTypes' => "uk\ac\ox\oppf\www\WSPlate\getPlateTypes", - 'getPlateTypesResponse' => "uk\ac\ox\oppf\www\WSPlate\getPlateTypesResponse", - 'ImagedPlateFault' => "uk\ac\ox\oppf\www\WSPlate\ImagedPlateFault", - 'imagedPlate' => "uk\ac\ox\oppf\www\WSPlate\imagedPlate", - 'imagedPlateResponse' => "uk\ac\ox\oppf\www\WSPlate\imagedPlateResponse", - 'ImageInfo' => "uk\ac\ox\oppf\www\WSPlate\ImageInfo", - 'ImageType' => "uk\ac\ox\oppf\www\WSPlate\ImageType", - 'ImagingPlateFault' => "uk\ac\ox\oppf\www\WSPlate\ImagingPlateFault", - 'imagingPlate' => "uk\ac\ox\oppf\www\WSPlate\imagingPlate", - 'imagingPlateResponse' => "uk\ac\ox\oppf\www\WSPlate\imagingPlateResponse", - 'ImagingTask' => "uk\ac\ox\oppf\www\WSPlate\ImagingTask", - 'ListProjectsFault' => "uk\ac\ox\oppf\www\WSPlate\ListProjectsFault", - 'listProjects' => "uk\ac\ox\oppf\www\WSPlate\listProjects", - 'listProjectsResponse' => "uk\ac\ox\oppf\www\WSPlate\listProjectsResponse", - 'ListSamplesFault' => "uk\ac\ox\oppf\www\WSPlate\ListSamplesFault", - 'listSamples' => "uk\ac\ox\oppf\www\WSPlate\listSamples", - 'listSamplesResponse' => "uk\ac\ox\oppf\www\WSPlate\listSamplesResponse", - 'PlateInfo' => "uk\ac\ox\oppf\www\WSPlate\PlateInfo", - 'PlateType' => "uk\ac\ox\oppf\www\WSPlate\PlateType", - 'Point' => "uk\ac\ox\oppf\www\WSPlate\Point", - 'ProcessingInfo' => "uk\ac\ox\oppf\www\WSPlate\ProcessingInfo", - 'Project' => "uk\ac\ox\oppf\www\WSPlate\Project", - 'Property' => "uk\ac\ox\oppf\www\WSPlate\Property", - 'Robot' => "uk\ac\ox\oppf\www\WSPlate\Robot", - 'Sample' => "uk\ac\ox\oppf\www\WSPlate\Sample", - 'Size' => "uk\ac\ox\oppf\www\WSPlate\Size", - 'SkippedImagingFault' => "uk\ac\ox\oppf\www\WSPlate\SkippedImagingFault", - 'skippedImaging' => "uk\ac\ox\oppf\www\WSPlate\skippedImaging", - 'skippedImagingResponse' => "uk\ac\ox\oppf\www\WSPlate\skippedImagingResponse", - 'SupportsPriorityFault' => "uk\ac\ox\oppf\www\WSPlate\SupportsPriorityFault", - 'supportsPriority' => "uk\ac\ox\oppf\www\WSPlate\supportsPriority", - 'supportsPriorityResponse' => "uk\ac\ox\oppf\www\WSPlate\supportsPriorityResponse", - 'UpdatedPriorityFault' => "uk\ac\ox\oppf\www\WSPlate\UpdatedPriorityFault", - 'updatedPriority' => "uk\ac\ox\oppf\www\WSPlate\updatedPriority", - 'updatedPriorityResponse' => "uk\ac\ox\oppf\www\WSPlate\updatedPriorityResponse", - 'UploadImage' => "uk\ac\ox\oppf\www\WSPlate\UploadImage", - 'UploadImageResponse' => "uk\ac\ox\oppf\www\WSPlate\UploadImageResponse", - 'UploadImagesFault' => "uk\ac\ox\oppf\www\WSPlate\UploadImagesFault", - 'uploadImages' => "uk\ac\ox\oppf\www\WSPlate\uploadImages", - 'uploadImagesResponse' => "uk\ac\ox\oppf\www\WSPlate\uploadImagesResponse", - 'WSPlateError' => "uk\ac\ox\oppf\www\WSPlate\WSPlateError" - ) - ) -); - -// WSDL operations - PlateInfoProvider -//getPlateID - not used -//getPlateInfo - required -//getPlateType - not used -//getPlateTypes - not used - -// WSDL operations - ImageProcessorProvider -//getImageProcessor - WSDL bug - should not be present - -// WSDL operations - ImagingTaskProvider -//getImagingTasks - required -//imagingPlate - required -//imagedPlate - required -//skippedImaging - required -//supportsPriority - not used -//updatedPriority - required - -// WSDL operations - CaptureProvider -//getDefaultCaptureProfile - not used -//getCapturePoints - not used -//getFirstDrop - not used - -// WSDL operations - SampleProvider -//uploadImages - required for ImageUploader -//listSamples - required for CartWrapper -//listProjects - required for CartWrapper -//canListSamples - required for CartWrapper - -// Implemented WSDL operations -$server->addFunction("getPlateInfo"); -$server->addFunction("getImagingTasks"); -$server->addFunction("imagingPlate"); -$server->addFunction("imagedPlate"); -$server->addFunction("skippedImaging"); -$server->addFunction("updatedPriority"); -$server->addFunction("uploadImages"); - -/******************************************************************* -********************** END SOAPSERVER SETUP ************************ -*******************************************************************/ - -/******************************************************************* -********************** HANDLE SOAP REQUESTS ************************ -*******************************************************************/ - -// Handle the SOAP request -try { - // ob_start(); - $server->handle(); - // $xml = ob_get_contents(); - // ob_end_clean(); - // error_log($xml); - // echo $xml; -} -catch (Exception $e) { - $server->fault('Sender', $e->getMessage()); -} - -?> - diff --git a/api/formulatrix/uploader/README b/api/formulatrix/uploader/README deleted file mode 100644 index ea2c79675..000000000 --- a/api/formulatrix/uploader/README +++ /dev/null @@ -1,6 +0,0 @@ -Run as i02-2detector -module load python/ana -python formulatrix_uploader.py -c config_ef.json -OR -python formulatrix_uploader.py -c config_z.json - diff --git a/api/formulatrix/uploader/config.json.sample b/api/formulatrix/uploader/config.json.sample deleted file mode 100644 index f364b0845..000000000 --- a/api/formulatrix/uploader/config.json.sample +++ /dev/null @@ -1,20 +0,0 @@ -{ - "user": "username", - "pw": "password", - "db": "database", - "host": "hostname", - "port": 3306, - "holding_dir": "/dls/holding/directory", - "upload_dir": "/dls/mx/data", - "upload_dir_old": "/dls/i02-2/data", - "web_user": "myuser", - "thumb_width": 150, - "thumb_height:": 120, - "types": { - "CrystalQuickX": { "well_per_row": 12, "drops_per_well": 2 } - } - "logging": { - "syslog": {"host": "my-server", "port": 514, "format": "fmlx_ul:%(message)s", "level": "error"}, - "rotating_file": {"filename": "fmlx_ul.log", "max_bytes": 1000000, "no_files": 20, "format": "* %(asctime)s [id=%(thread)d] <%(levelname)s> %(message)s", "level": "debug"} - } -} diff --git a/api/formulatrix/uploader/config_ef.json.sample b/api/formulatrix/uploader/config_ef.json.sample deleted file mode 100644 index 606bffe1d..000000000 --- a/api/formulatrix/uploader/config_ef.json.sample +++ /dev/null @@ -1,25 +0,0 @@ -{ - "task": "EF", - "user": "username", - "pw": "password", - "db": "database", - "host": "hostname", - "holding_dir": "/dls/holding/directory", - "archive_dir": "/dls/archive/directory", - "upload_dir": "/dls/mx/data", - "upload_dir_old": "/dls/i02-2/data", - "web_user": "myuser", - "thumb_width": 200, - "thumb_height": 150, - "types": { - "CrystalQuickX": { "well_per_row": 12, "drops_per_well": 2 }, - "MitegenInSitu": { "well_per_row": 12, "drops_per_well": 2 }, - "FilmBatch": { "well_per_row": 12, "drops_per_well": 1 }, - "ReferencePlate": { "well_per_row": 2, "drops_per_well": 1 } - }, - "logging": { - "rotating_file": {"filename": "/tmp/fmlx_ul_ef.log", "max_bytes": 1000000, "no_files": 20, "format": "* %(asctime)s [id=%(thread)d] <%(levelname)s> %(message)s", "level": "debug"}, - "syslog": {"host": "my-server", "port": 514, "format": "fmlx_ul_ef:%(message)s", "level": "error"} - }, - "pid_file": "/tmp/formulatrix_uploader_ef.pid" -} diff --git a/api/formulatrix/uploader/config_z.json.sample b/api/formulatrix/uploader/config_z.json.sample deleted file mode 100644 index 35677675b..000000000 --- a/api/formulatrix/uploader/config_z.json.sample +++ /dev/null @@ -1,25 +0,0 @@ -{ - "task": "Z", - "user": "username", - "pw": "password", - "db": "database", - "host": "hostname", - "holding_dir": "/dls/holding/directory", - "archive_dir": "/dls/archive/directory", - "upload_dir": "/dls/mx/data", - "upload_dir_old": "/dls/i02-2/data", - "web_user": "myuser", - "thumb_width": 200, - "thumb_height": 150, - "types": { - "CrystalQuickX": { "well_per_row": 12, "drops_per_well": 2 }, - "MitegenInSitu": { "well_per_row": 12, "drops_per_well": 2 }, - "FilmBatch": { "well_per_row": 12, "drops_per_well": 1 }, - "ReferencePlate": { "well_per_row": 2, "drops_per_well": 1 } - }, - "logging": { - "rotating_file": {"filename": "/tmp/fmlx_ul_z.log", "max_bytes": 1000000, "no_files": 20, "format": "* %(asctime)s [id=%(thread)d] <%(levelname)s> %(message)s", "level": "debug"}, - "syslog": {"host": "my-server", "port": 514, "format": "fmlx_ul_z:%(message)s", "level": "error"} - }, - "pid_file": "/tmp/formulatrix_uploader_z.pid" -} diff --git a/api/formulatrix/uploader/formulatrix_uploader.py b/api/formulatrix/uploader/formulatrix_uploader.py deleted file mode 100644 index 7a64928ec..000000000 --- a/api/formulatrix/uploader/formulatrix_uploader.py +++ /dev/null @@ -1,490 +0,0 @@ -# -*- coding: utf-8 -*- -#!/usr/bin/env python -# -# Copyright 2015 Diamond Light Source -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Implementation of Jon Diprose's ImageUploader in Python for -# formulatrix plate imagers, takes outputted xml / image files and -# puts them in the correct location, and adds an entry to SynchWeb - -import json -import time -import glob -import re -import os -import sys -import atexit -import signal -import errno -import subprocess -import logging -import logging.handlers -import MySQLdb -from PIL import Image -from shutil import copyfile -import xml.etree.ElementTree as ET -import shutil -import getopt - -class MySQL: - - def __init__(self, user, pw, db, host='127.0.0.1', port=3306): - self._conn = MySQLdb.connect(host=host, user=user, passwd=pw, db=db, port=port) - self._conn.autocommit(1) - self._conn.ping(True) - - self._cur = self._conn.cursor(MySQLdb.cursors.DictCursor) - - - def __del__(self): - if self._cur is not None: - self._cur.close() - - if self._conn is not None: - self._conn.close() - - - def pq(self, query, args=[]): - res = self._cur.execute(query, args) - - rows = [] - for r in self._cur: - rows.append(r) - - return rows if rows else [] - - - def id(self): - return self._cur.connection.insert_id() - - - -class FormulatrixUploader: - - _running = True - - def __init__(self, db=None, config=None): - self.db = db - self.config = config - - for d in ['processed', 'nosample']: - if not os.path.exists(config['holding_dir']+'/'+d): - os.mkdir(config['holding_dir']+'/'+d) - - def _move_dir(self, src_dir, target_dir): - """This will overwrite any existing files with the same names. - Make sure files are completed (written, closed) before moving them.""" - - logging.getLogger().debug("trying to glob.glob('%s/*')" % src_dir) - files = glob.glob("%s/*" % src_dir) - for f in files: - st = os.stat(f) - if time.time() - st.st_mtime > 10 and st.st_size > 0: - new_f = os.path.join(target_dir, os.path.basename(f)) - logging.getLogger().debug('copy: %s to %s' % (f, new_f)) - try: - shutil.copyfile(f, new_f) - try: - os.unlink(f) - except IOError as e: - logging.getLogger().error('Error deleting image file %s' % f) - except IOError as e: - logging.getLogger().error('Error copying image file %s to %s' % (f, new_f)) - else: - logging.getLogger().debug('Not moving file %s yet as it there is a handle on it' % f) - - # Remove the src_dir if empty - self._rmdir(src_dir) - - def _rmdir(self, dir): - """rmdir the dir (only works if it's empty)""" - try: - os.rmdir(dir) - except OSError as e: - pass - - def _get_most_recent_container_dirs(self, dirs): - """Generate a dict of all containers with their most recent z-slice directories (dates)""" - containers = dict() - for dir in dirs: - dir_containers = glob.glob(dir+"/*/") - for dir_container in dir_containers: - barcode = os.path.basename(os.path.abspath(dir_container)) - containers[barcode] = dir - return containers - - def _get_visit_dir(self, container): - visit = container['visit'] - proposal = visit[ : visit.index('-')] - new_root = '{root}/{proposal}/{visit}'.format(root=self.config['upload_dir'], proposal=proposal, visit=visit) - old_root = '{root}/{year}/{visit}'.format(root=self.config['upload_dir_old'], year=container['year'], visit=visit) - - the_root = None - if os.path.exists(new_root): - the_root = new_root - elif os.path.exists(old_root): - the_root = old_root - else: - logging.getLogger().error('Visit directory for visit doesnt exist, tried %s and %s' % (new_root, old_root)) - return None - - return the_root - - def _make_dirs(self, path): - if not os.path.exists(path): - - try: - os.makedirs(path) - if config['web_user']: - subprocess.call(['/usr/bin/setfacl', '-R', '-m', 'u:'+config['web_user']+':rwx', path]); - - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(new_path): - pass - elif exc.errno == errno.EACCES: - logging.getLogger().error("%s - %s" % (exc.strerror, new_path)) - return False - else: - raise - return True - - def handle_zslice_images(self): - """Move the z-slice images from the configured 'archive_dir' to their target_dir which is a folder - named by the container barcode in the tmp folder in the container's visit dir.""" - - date_dirs = glob.glob(self.config['archive_dir']+"/*/") - container_dict = self._get_most_recent_container_dirs(date_dirs) - - # Move the files in the most recent container imaging dirs within the archive_dir - for barcode in container_dict: - container = self._get_container_by_barcode(barcode) - if container['visit'] is None: - logging.getLogger().error('Container barcode %s has no session' % (str(barcode)) ) - continue - - # Determine the container's target directory - visit_dir = self._get_visit_dir(container) - if visit_dir is None: - continue - target_dir = os.path.join(visit_dir, "tmp", barcode) - - if not self._make_dirs(target_dir): - continue - - # Move all the files (overwrite any existing files) in the barcode dir to the target_dir - src_dir = os.path.join(container_dict[barcode], barcode) - self._move_dir(src_dir, target_dir) - - # Delete all non-recent container imaging dirs within the archive_dir - recent_container_dirs = [] - for barcode in container_dict: - recent_container_dirs.append(os.path.join(container_dict[barcode], barcode)) - - all_container_dirs = glob.glob(self.config['archive_dir']+"/*/*/") - for dir in all_container_dirs: - # Remove the last character ("/") from the dir when comparing - if dir[:-1] not in recent_container_dirs: - try: - logging.getLogger().debug("trying to rmtree(%s)" % (dir)) - shutil.rmtree(dir) - except OSError as oe: - logging.getLogger().error("OSError in shutil.rmtree('%s')" % dir) - - # Remove date folders if empty - for dir in date_dirs: - self._rmdir(dir) - - - def handle_ef_images(self): - """Move extended focus (EF) images from the configuration holding_dir to - imaging/{containerid}/{inspectionid} within the container's visit dir. - Also create thumbnail images.""" - files = glob.glob(self.config['holding_dir']+"/*EF*.xml") - for xml in files: - logging.getLogger().debug(xml) - st = os.stat(xml) - - image = xml.replace('.xml', '.jpg') - if not os.path.exists(image): - logging.getLogger().error('Corresponding image not found for %s expected %s' % (str(xml), str(image)) ) - continue - - if time.time() - st.st_mtime > 10 and st.st_size > 0: - tree = ET.parse(xml) - root = tree.getroot() - - # deal with xml namespace - ns = root.tag.split('}')[0].strip('{') - nss = { 'oppf': ns } - - inspectionid = re.sub('\-.*', '', root.find('oppf:ImagingId', nss).text) - - logging.getLogger().debug('inspection: %s' % str(inspectionid)) - - container = self._get_container(inspectionid) - if container is None: - continue - - # Check if the visit dir exists yet - the_root = self._get_visit_dir(container) - if the_root is None: - continue - - # Keep images in visit/imaging/containerid/inspectionid - new_path = '{the_root}/imaging/{containerid}/{inspectionid}'.format(the_root=the_root, containerid=container['containerid'], inspectionid=inspectionid) - if not self._make_dirs(new_path): - continue - - position = self._get_position(root.find('oppf:Drop', nss).text, container['containertype']) - if position is None: - logging.getLogger().error('Could not match drop: %s to position: %s' % (root.find('oppf:Drop', nss).text, container['containertype']) ) - continue - - logging.getLogger().debug('Drop: %s position: %s' % (root.find('oppf:Drop', nss).text, position)) - - sampleid = self._get_sampleid(position, container['containerid']) - if sampleid is None: - self._move_files(image, xml, 'nosample') - continue - - mppx = float(root.find('oppf:SizeInMicrons', nss).find('oppf:Width', nss).text) / float(root.find('oppf:SizeInPixels', nss).find('oppf:Width', nss).text) - mppy = float(root.find('oppf:SizeInMicrons', nss).find('oppf:Height', nss).text) / float(root.find('oppf:SizeInPixels', nss).find('oppf:Height', nss).text) - - db.pq("""INSERT INTO BLSampleImage (blsampleid, micronsperpixelx, micronsperpixely, containerinspectionid) - VALUES (%s,%s,%s,%s)""", [sampleid, mppx, mppy, inspectionid]) - logging.getLogger().debug("INSERT INTO BLSampleImage "\ - "(blsampleid, micronsperpixelx, micronsperpixely, containerinspectionid) "\ - "VALUES (%s,%s,%s,%s)" % (str(sampleid), str(mppx), str(mppy), str(inspectionid))) - - iid = db.id() - - # Use blsampleimageid as file name as we are sure this is unique - new_file = '{path}/{iid}.jpg'.format(path=new_path, iid=iid) - - db.pq("""UPDATE BLSampleImage set imagefullpath=%s WHERE blsampleimageid=%s""", [new_file, iid]) - logging.getLogger().debug("UPDATE BLSampleImage set imagefullpath=%s WHERE blsampleimageid=%s" % (new_file, str(iid))) - - # move image - logging.getLogger().debug('copy: %s to %s' % (image, new_file)) - try: - copyfile(image, new_file) - - # create a thumbnail - file, ext = os.path.splitext(new_file) - try: - im = Image.open(new_file) - im.thumbnail((config['thumb_width'], config['thumb_height'])) - try: - im.save(file+'th'+ext) - except IOError as e: - logging.getLogger().error('Error saving image file %s' % file+'th'+ext) - # clear up - should be in a try ... except? - #self._move_files(image, xml, 'processed') - try: - os.unlink(image) - except IOError as e: - logging.getLogger().error('Error deleting image file %s' % image) - try: - os.unlink(xml) - except IOError as e: - logging.getLogger().error('Error deleting XML file %s' % xml) - except IOError as e: - logging.getLogger().error('Error opening image file %s' % new_file) - except IOError as e: - logging.getLogger().error('Error copying image file %s to %s' % (image, new_file)) - - - def _move_files(self, image, xml, path): - for f in [image, xml]: - os.rename(f, f.replace(self.config['holding_dir'], self.config['holding_dir']+'/'+path)) - logging.getLogger().debug('move %s %s' % (f, f.replace(self.config['holding_dir'], self.config['holding_dir']+'/'+path))) - - def _get_container_by_barcode(self, barcode): - container = self.db.pq("""SELECT c.containertype, c.containerid, c.sessionid, CONCAT(p.proposalcode, p.proposalnumber, '-', bs.visit_number) as visit, DATE_FORMAT(c.bltimestamp, '%%Y') as year - FROM Container c - LEFT OUTER JOIN BLSession bs ON bs.sessionid = c.sessionid - LEFT OUTER JOIN Proposal p ON p.proposalid = bs.proposalid - WHERE c.barcode=%s - LIMIT 1""", [barcode]) - - if not len(container): - logging.getLogger().error('Couldn\'t find container in database for barcode %s' % str(barcode)) - return None - - logging.getLogger().debug(str(container[0]['visit'])) - - return container[0] - - - def _get_container(self, inspectionid): - container = self.db.pq("""SELECT c.containertype, c.containerid, c.sessionid, CONCAT(p.proposalcode, p.proposalnumber, '-', bs.visit_number) as visit, DATE_FORMAT(c.bltimestamp, '%%Y') as year - FROM Container c - INNER JOIN ContainerInspection ci ON ci.containerid = c.containerid - INNER JOIN Dewar d ON d.dewarid = c.dewarid - INNER JOIN Shipping s ON s.shippingid = d.shippingid - INNER JOIN Proposal p ON p.proposalid = s.proposalid - LEFT OUTER JOIN BLSession bs ON bs.sessionid = c.sessionid - WHERE ci.containerinspectionid=%s - LIMIT 1""", [inspectionid]) - - if not len(container): - logging.getLogger().error('Couldn\'t find container for inspectionid %s' % str(inspectionid)) - return - - logging.getLogger().debug(str(container)) - - if not container[0]['sessionid']: - logging.getLogger().error('Container %s has no sessionid. inspectionid is %s ' % (str(container[0]['containerid']), str(inspectionid))) - return - - return container[0] - - - def _get_position(self, text_position, platetype): - well, drop = text_position.split('.') - - drop = int(drop) - row = ord(well[0])-65 - col = int(well[1:])-1 - - # Need to know what type of plate this is to know how many columns its got - # This should be in the database, currently in json format embedded in this collection: - # http://ispyb.diamond.ac.uk/beta/client/js/modules/shipment/collections/platetypes.js - if not platetype in self.config['types']: - logging.getLogger().error('Unknown plate type: %s' % platetype) - return - - ty = self.config['types'][platetype] - - # Position is a linear sequence left to right across the plate - return (ty['well_per_row']*row*ty['drops_per_well']) + (col*ty['drops_per_well']) + (drop-1) + 1 - - - # Return a blsampleid from a position and containerid - def _get_sampleid(self, position, containerid): - sample = self.db.pq("""SELECT s.blsampleid, s.name, s.location - FROM BLSample s - INNER JOIN Container c ON c.containerid = s.containerid - WHERE s.location = %s AND c.containerid = %s - LIMIT 1""", [position, containerid]) - - if not len(sample): - logging.getLogger().error('Couldn\'t find a blsample for containerid: %s, position: %s', str(containerid), str(position)) - return - - logging.getLogger().debug(str(sample[0])) - - return sample[0]['blsampleid'] - - -def kill_handler(sig,frame): - hostname = os.uname()[1] - logging.getLogger().warning("%s: got SIGTERM on %s :-O" % (sys.argv[0], hostname)) - logging.shutdown() - os._exit(-1) - -def set_logging(logs): - levels_dict = {"debug" : logging.DEBUG, "info" : logging.INFO, "warning" : logging.WARNING, "error" : logging.ERROR, "critical" : logging.CRITICAL} - - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - for log_name in logs: - handler = None - if log_name == "syslog": - handler = logging.handlers.SysLogHandler(address=(logs[log_name]['host'], logs[log_name]['port'])) - elif log_name == "rotating_file": - handler = logging.handlers.RotatingFileHandler(filename=logs[log_name]['filename'], maxBytes=logs[log_name]['max_bytes'], backupCount=logs[log_name]['no_files']) - else: - sys.exit("Invalid logging mechanism defined in config: %s. (Valid options are syslog and rotating_file.)" % log_name) - - handler.setFormatter(logging.Formatter(logs[log_name]['format'])) - level = logs[log_name]['level'] - if levels_dict[level]: - handler.setLevel(levels_dict[level]) - else: - handler.setLevel(logging.WARNING) - logger.addHandler(handler) - -def clean_up(): - global pid_file - os.unlink(pid_file) - logging.getLogger().info("%s: exiting python interpreter :-(" % sys.argv[0]) - logging.shutdown() - -def print_usage(): - usage = """Script for uploading image files from Rock Imager into the correct session directories. -Syntax: %s -c [-rp] -Arguments: - -h|--help : display this help - -c|--conf : use the given configuration file, default is config_ef.json""" % sys.argv[0] - - print usage - -global pid_file -pid_file = None -conf_file = 'config_ef.json' -log_file = None - -# Get command-line arguments -try: - opts, args = getopt.gnu_getopt(sys.argv[1:], "hc:", ["help", "conf"]) -except getopt.GetoptError: - print_usage() - sys.exit(2) - -for o,a in opts: - if o in ("-h", "--help"): - print_usage() - sys.exit() - elif o in ("-c", "--conf"): - conf_file = a - -cf = open(conf_file) -config = json.load(cf) -cf.close() - -if config['task'] not in ('EF', 'Z'): - print_usage() - sys.exit() - -set_logging(config['logging']) - -signal.signal(signal.SIGTERM, kill_handler) - -# Create a pid file -pid_file = config['pid_file'] -if os.path.isfile(pid_file): - logging.getLogger().error("%s already exists, exiting" % pid_file) - sys.exit() - -if pid_file != None: - try: - f = open(pid_file, 'w') - f.write(str(os.getpid())) - f.close() - except: - logging.getLogger().error("Unable to write to pid file %s" % pid_file) - -atexit.register(clean_up) # Remove pid file when exiting -atexit.register(logging.shutdown) - - -db = MySQL(user=config['user'], pw=config['pw'], db=config['db'], host=config['host'], port=int(config['port'])) - -uploader = FormulatrixUploader(db=db, config=config) -if config['task'] == 'EF': - uploader.handle_ef_images() -elif config['task'] == 'Z': - uploader.handle_zslice_images() diff --git a/api/index.php b/api/index.php index 29ad8c5ab..45435ea84 100644 --- a/api/index.php +++ b/api/index.php @@ -1,25 +1,30 @@ - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ + Copyright 2015 Diamond Light Source + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ use Slim\Slim; -use SynchWeb\Authentication; -use SynchWeb\Database\Type\MySQL; +use SynchWeb\Controllers\AuthenticationController; +use SynchWeb\Controllers\UserController; +use SynchWeb\Controllers\AssignController; +use SynchWeb\Model\Services\AuthenticationData; +use SynchWeb\Model\Services\UserData; +use SynchWeb\Model\Services\AssignData; +use SynchWeb\Database\DatabaseFactory; +use SynchWeb\Database\DatabaseConnectionFactory; +use SynchWeb\Database\DatabaseParent; +use SynchWeb\ImagingShared; use SynchWeb\Dispatch; -use SynchWeb\User; +use SynchWeb\Options; require 'vendor/autoload.php'; @@ -29,56 +34,121 @@ session_cache_limiter(false); -$app = new Slim(array( - 'mode' => $mode == 'production' ? 'production' : 'development' -)); - -$app->configureMode('production', function () use ($app) { - $app->config(array( - 'log.enable' => true, - 'debug' => false - )); -}); - -$app->configureMode('development', function () use ($app) { - $app->config(array( - 'log.enable' => false, - 'debug' => true - )); -}); - -$app->get('/options', function () use ($app) { - global $motd, $authentication_type, $cas_url, $cas_sso, $package_description, $facility_courier_countries, $facility_courier_countries_nde, $dhl_enable, $dhl_link, $scale_grid, $preset_proposal, $timezone, $valid_components, $enabled_container_types; - $app->contentType('application/json'); - $app->response()->body(json_encode(array('motd' => $motd, 'authentication_type' => $authentication_type, 'cas_url' => $cas_url, 'cas_sso' => $cas_sso, 'package_description' => $package_description, 'facility_courier_countries' => $facility_courier_countries, 'facility_courier_countries_nde' => $facility_courier_countries_nde, 'dhl_enable' => $dhl_enable, 'dhl_link' => $dhl_link, 'scale_grid' => $scale_grid, 'preset_proposal' => $preset_proposal, 'timezone' => $timezone, 'valid_components' => $valid_components, 'enabled_container_types' => $enabled_container_types))); -}); - -// the following prevents unexpected effects when using objects as save handlers -register_shutdown_function('session_write_close'); +$app = setupApplication($mode); -$port = array_key_exists('port', $isb) ? $isb['port'] : null; +register_shutdown_function('session_write_close'); // prevents unexpected effects when using objects as save handlers -// MySQL database class hard-coded -$db = new MySQL($isb['user'], $isb['pass'], $isb['db'], $port); +setupDependencyInjectionContainer($app); -// Alternatively, use dynamic class instantiation. -// Database type ($dbtype) specified in config.php. -// $db = Database::get(); +$auth = $app->container['auth']; +$auth->validateAuthentication(); +$auth->updateActivityTimestamp(); -$db->set_app($app); +$app->container['dispatch']->dispatch(); -$auth = new Authentication($app, $db); -$auth->check_auth_required(); - -$login = $auth->get_user(); -$user = new User($login, $db, $app); +function setupApplication($mode): Slim +{ + $app = new Slim(array( + 'mode' => $mode == 'production' ? 'production' : 'development' + )); -if ($user->login) { - $chk = $db->pq("SELECT TIMESTAMPDIFF('SECOND', datetime, CURRENT_TIMESTAMP) AS lastupdate, comments FROM adminactivity WHERE username LIKE :1", array($user->login)); - if (sizeof($chk)) { - if ($chk[0]['LASTUPDATE'] > 20) $db->pq("UPDATE adminactivity SET datetime=CURRENT_TIMESTAMP WHERE username=:1", array($user->login)); - } + $app->configureMode('production', function () use ($app) { + $app->config(array( + 'log.enable' => true, + 'debug' => false + )); + }); + + $app->configureMode('development', function () use ($app) { + $app->config(array( + 'log.enable' => false, + 'debug' => true + )); + }); + + $app->get('/options', function () use ($app) { + global $motd, $authentication_type, $cas_url, $cas_sso, $sso_url, $package_description, + $facility_courier_countries, $facility_courier_countries_nde, + $dhl_enable, $dhl_link, $scale_grid, $scale_grid_end_date, $preset_proposal, $timezone, + $valid_components, $enabled_container_types, $ifsummary; + $app->contentType('application/json'); + $options = $app->container['options']; + $app->response()->body(json_encode(array( + 'motd' => $options->get('motd', $motd), + 'authentication_type' => $authentication_type, + 'cas_url' => $cas_url, + 'cas_sso' => $cas_sso, + 'sso_url' => $sso_url, + 'package_description' => $package_description, + 'facility_courier_countries' => $facility_courier_countries, + 'facility_courier_countries_nde' => $facility_courier_countries_nde, + 'dhl_enable' => $dhl_enable, + 'dhl_link' => $dhl_link, + 'scale_grid' => $scale_grid, + 'scale_grid_end_date' => $scale_grid_end_date, + 'preset_proposal' => $preset_proposal, + 'timezone' => $timezone, + 'valid_components' => $valid_components, + 'enabled_container_types' => $enabled_container_types, + 'ifsummary' => $ifsummary + ))); + }); + return $app; } -$type = new Dispatch($app, $db, $user); -$type->dispatch(); +function setupDependencyInjectionContainer($app) +{ + $app->container->singleton('db', function () use ($app): DatabaseParent { + $dbFactory = new DatabaseFactory(new DatabaseConnectionFactory()); + $db = $dbFactory->get(); + $db->set_app($app); + return $db; + }); + + $app->container->singleton('dbsummary', function () use ($app): DatabaseParent { + $dbFactory = new DatabaseFactory(new DatabaseConnectionFactory()); + $db = $dbFactory->get("summary"); + $db->set_app($app); + return $db; + }); + + $app->container->singleton('authData', function () use ($app) { + return new AuthenticationData($app->container['db']); + }); + + $app->container->singleton('auth', function () use ($app) { + return new AuthenticationController($app, $app->container['authData']); + }); + + $app->container->singleton('user', function () use ($app) { + return $app->container['auth']->getUser(); + }); + + $app->container->singleton('userData', function () use ($app) { + return new UserData($app->container['db']); + }); + + $app->container->singleton('userController', function () use ($app) { + return new UserController($app, $app->container['db'], $app->container['user'], $app->container['userData']); + }); + + $app->container->singleton('assignData', function () use ($app) { + return new AssignData($app->container['db']); + }); + + $app->container->singleton('assignController', function () use ($app) { + return new AssignController($app, $app->container['db'], $app->container['user'], $app->container['assignData']); + }); + + $app->container->singleton('imagingShared', function () use ($app) { + return new ImagingShared($app->container['db']); + }); + + $app->container->singleton('dispatch', function () use ($app) { + return new Dispatch($app, $app->container['db'], $app->container['user']); + }); + + $app->container->singleton('options', function () use ($app) { + return new Options($app->container['db']); + }); +} diff --git a/api/psalm.xml b/api/psalm.xml new file mode 100644 index 000000000..899614d54 --- /dev/null +++ b/api/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/api/scripts/cbf2jpg.sh b/api/scripts/cbf2jpg.sh deleted file mode 100755 index 39e953968..000000000 --- a/api/scripts/cbf2jpg.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -# -# This script is deprecated at DLS -# -#. /etc/profile.d/modules.sh -#module load ccp4 -#module load mosflm - -cd /tmp -find /tmp -mtime +3 -delete - -export CCP4_MASTER=/dls_sw/apps/ccp4/7.0.052 -export CINCL=$CCP4_MASTER/ccp4-7.0/include -export CLIBD=$CCP4_MASTER/ccp4-7.0/lib/data -export CCP4_SCR=/tmp - -/dls_sw/apps/mosflm/7.2.2-29nov2016/ipmosflm << eof -#ipmosflm << eof -DETECTOR PILATUS -XGUI ON -DIRECTORY $1 -TEMPLATE $2 -IMAGE $3 -GO -CREATE_IMAGE ZOOM -1 BINARY TRUE FILENAME $4 -RETURN -EXIT -eof - -rm fort.8 diff --git a/api/scripts/dls/environ.sh b/api/scripts/dls/environ.sh deleted file mode 100644 index a73c9f281..000000000 --- a/api/scripts/dls/environ.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# This script is deprecated at DLS. -# It was required for other scripts that are now deprecated. -# -# CCP4 - -. /dls_sw/apps/ccp4/64/7.0/update35/ccp4-7.0/bin/ccp4.setup-sh - -# XDS - -export PATH=/dls_sw/apps/XDS/20170215/:${PATH} - -# DIALS - -. /dls_sw/apps/dials/dials-v1-5-1/build/setpaths.sh - diff --git a/api/scripts/dls/reprocess.sh b/api/scripts/dls/reprocess.sh deleted file mode 100644 index 2064fd714..000000000 --- a/api/scripts/dls/reprocess.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -# -# This script is deprecated at DLS. -# Reprocessing takes place via the Zocalo ActiveMQ architecture -# -#export CINCL=/dls_sw/apps/ccp4/64/7.0/update35/ccp4-7.0/include -#export CCP4=/dls_sw/apps/ccp4/64/7.0/update35/ccp4-7.0 -#export CLIBD=/dls_sw/apps/ccp4/64/7.0/update35/ccp4-7.0/lib/data - -. /dls_sw/apps/mx-scripts/ispyb-reprocess/environ.sh - -# Dials hotfix -export PYTHON_EGG_CACHE=/tmp/pythoneggcache -mkdir -p $PYTHON_EGG_CACHE - -while [[ $# -gt 1 ]] -do -key="$1" - -case $key in - -p|--pipeline) - PIPELINE="$2" - shift - ;; - -c|--unitcell) - UNITCELL="unit_cell=$2" - shift - ;; - -g|--spacegroup) - SPACEGROUP="space_group=$2" - shift - ;; - -r|--resolution) - RESOLUTION="d_min=$2" - shift - ;; - -i|--dcid) - DCID="$2" - shift - ;; - -d|--workingdir) - CWD="$2" - shift - ;; - *) - - ;; -esac -shift -done - - -# module load xia2 - -# Fix for plate data on i03 -echo 'xds.colspot.minimum_pixels_per_spot=3' > spot.phil - -xia2 failover=True pipeline=${PIPELINE} ${SPACEGROUP} ${UNITCELL} ${RESOLUTION} xinfo=xia.xinfo spot.phil -xia2.ispyb_xml ${CWD}/ispyb_reproc.xml -sed -e 's/xia2.txt/xia2.html/' -e 's/[^>]*>//g' -e 's//'"${DCID}"'<\/dataCollectionId>/' -e 's/[^>]*>//g' ispyb_reproc.xml > ispyb_reproc2.xml - - -# FIXME I think this needs to use module load python/ana python yesno? -python /dls_sw/apps/mx-scripts/dbserver/src/DbserverClient.py -h sci-serv3 -p 1994 -i ${CWD}/ispyb_reproc2.xml - diff --git a/api/scripts/dls/submit.sh b/api/scripts/dls/submit.sh deleted file mode 100644 index 2d28e6526..000000000 --- a/api/scripts/dls/submit.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# -# This script is deprecated at DLS. -# -. /etc/profile.d/modules.sh -module load global/cluster - -qsub -cwd -pe smp 16 -q medium.q@@com09 $1 - diff --git a/api/scripts/mtz2map.sh b/api/scripts/mtz2map.sh index e6f1df143..04fb213c0 100755 --- a/api/scripts/mtz2map.sh +++ b/api/scripts/mtz2map.sh @@ -15,15 +15,35 @@ export CLIBD=$CCP4_MASTER/lib/data export CCP4_SCR=/tmp export root=$CCP4_MASTER/bin +if [ -f $1 ]; then + mtz=$1 +else + if [ -f $1.gz ]; then + mtz=/tmp/$2_$3.mtz + gunzip -c $1.gz > $mtz + else + echo "No mtz file found" + exit + fi +fi + if [ $3 == 'dimple' -o $3 == 'mrbump' ]; then +if [ -f $4 ]; then + pdb=$4 +else + if [ -f $4.gz ]; then + pdb=/tmp/$2_$3.pdb + gunzip -c $4.gz > $pdb + else + echo "No pdb file found" + exit + fi +fi + if [ $3 == 'dimple' ]; then - # fofc2="F1=F SIG1=SIGF PHI=PH2FOFCWT W=FOM" - # fofc="F1=F SIG1=SIGF PHI=PHFOFCWT W=FOM" - # fofc2="F1=F SIG1=SIGF PHI=PHWT W=FOM" - # fofc="F1=F SIG1=SIGF PHI=PHDELWT W=FOM" - if $root/mtzinfo $1 | grep -q PH2FOFCWT; then + if $root/mtzinfo $mtz | grep -q PH2FOFCWT; then fofc2="F1=2FOFCWT SIG1=SIGF PHI=PH2FOFCWT" fofc="F1=F SIG1=SIGF PHI=PHFOFCWT W=FOM" else @@ -37,7 +57,7 @@ fi # F SIGF FC PHIC FC_ALL PHIC_ALL FWT PHWT DELFWT PHDELWT FOM FC_ALL_LS PHIC_ALL_LS -$root/fft HKLIN $1 MAPOUT "/tmp/$2_$3_2fofc.map.tmp" << eof +$root/fft HKLIN $mtz MAPOUT "/tmp/$2_$3_2fofc.map.tmp" << eof title $2 2fofc xyzlim asu scale F1 1.0 @@ -46,11 +66,11 @@ $fofc2 end eof -$root/mapmask MAPIN "/tmp/$2_$3_2fofc.map.tmp" MAPOUT "/tmp/$2_$3_2fofc.map" XYZIN "$4" << eof +$root/mapmask MAPIN "/tmp/$2_$3_2fofc.map.tmp" MAPOUT "/tmp/$2_$3_2fofc.map" XYZIN "$pdb" << eof BORDER 5 eof -$root/fft HKLIN $1 MAPOUT "/tmp/$2_$3_fofc.map.tmp" << eof +$root/fft HKLIN $mtz MAPOUT "/tmp/$2_$3_fofc.map.tmp" << eof title $2 fofc xyzlim asu scale F1 1.0 @@ -60,16 +80,17 @@ end eof -$root/mapmask MAPIN "/tmp/$2_$3_fofc.map.tmp" MAPOUT "/tmp/$2_$3_fofc.map" XYZIN "$4" << eof +$root/mapmask MAPIN "/tmp/$2_$3_fofc.map.tmp" MAPOUT "/tmp/$2_$3_fofc.map" XYZIN "$pdb" << eof BORDER 5 eof gzip "/tmp/$2_$3_2fofc.map" gzip "/tmp/$2_$3_fofc.map" +rm -f /tmp/$2_$3.pdb else -$root/fft HKLIN $1 MAPOUT "/tmp/$2_$3.map" << eof +$root/fft HKLIN $mtz MAPOUT "/tmp/$2_$3.map" << eof title $2 fofc xyzlim asu scale F1 1.0 @@ -78,10 +99,8 @@ F1=F SIG1=SIGF PHI=PHI W=FOM end eof -#$mm MAPIN "/tmp/$2_ep.map.tmp" MAPOUT "/tmp/$2_ep.map" XYZIN "$4" << eof -#BORDER 5 -#eof - gzip "/tmp/$2_$3.map" fi + +rm -f /tmp/$2_$3.mtz diff --git a/api/src/Authentication.php b/api/src/Authentication.php deleted file mode 100644 index e379e8d31..000000000 --- a/api/src/Authentication.php +++ /dev/null @@ -1,296 +0,0 @@ -app = $app; - $this->db = $db; - - $this->app->post('/authenticate', array(&$this, 'authenticate'))->conditions(array( - 'login' => '[A-z0-9]+', - 'password' => '.*', - )); - - $this->app->get('/authenticate/check', array(&$this, 'check')); - $this->app->get('/authenticate/key', array(&$this, 'generate_jwt_key')); - $this->app->get('/authenticate/logout', array(&$this, 'logout')); - } - - - function get_user() { - return $this->user; - } - - - // For SSO check if we are already logged in elsewhere - // - if a mechanism exists to do this - function check() - { - global $authentication_type; - - $user = $this->authenticateByType($authentication_type)->check(); - - if (!$user) $this->_error(400, 'No previous session'); - - $userc = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($user)); - - if (sizeof($userc)) $this->_output(200, $this->generate_jwt($user)); - else $this->_error(400, 'No previous session'); - } - - - // Check if this request needs authentication - // We allow some pages unauthorised access based on IP, or calendar hash - function check_auth_required() { - global $blsr, $bcr, $img, $auto; - - $parts = explode('/', $this->app->request->getResourceUri()); - if (sizeof($parts)) array_shift($parts); - - $need_auth = true; - # Work around to allow beamline sample registration without CAS authentication - if (sizeof($parts) >= 3) { - if ( - # Calendar ICS export - ($parts[0] == 'cal' && $parts[1] == 'ics' && $parts[2] == 'h') || - - # Allow formulatrix machines unauthorised access to inspections, certain IPs only - ($parts[0] == 'imaging' && $parts[1] == 'inspection' && in_array($_SERVER["REMOTE_ADDR"], $img)) || - - # For use on the touchscreen computers in the hutch. - # Handles api calls: /assign/visits/ e.g./assign/visits/mx1234-1 - ($parts[0] == 'assign' && $parts[1] == 'visits' && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || - - # Allow barcode reader ips unauthorised access to add history - ($parts[0] == 'shipment' && $parts[1] == 'dewars' && $parts[2] == 'history' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || - - # Allow barcode reader ips unauthorised access to add comments - ($parts[0] == 'shipment' && $parts[1] == 'dewars' && $parts[2] == 'comments' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || - - # Container notification: allow beamlines running in automated mode to notify users - ($parts[0] == 'shipment' && $parts[1] == 'containers' && $parts[2] == 'notify' && in_array($_SERVER["REMOTE_ADDR"], $auto)) || - - # Allow barcode reader ips unauthorised access to add container history - ($parts[0] == 'shipment' && $parts[1] == 'containers' && $parts[2] == 'history' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) - ) { - $need_auth = false; - } - - } else if (sizeof($parts) >= 2) { - if ( - # For use on the touchscreen computers in the hutch - # Handles api calls: /assign/assign, /assign/unassign, /assign/deact, /assign/visits - (($parts[0] == 'assign') && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || - (($parts[0] == 'shipment' && $parts[1] == 'containers') && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || - - # Allow barcode reader unauthorised access, same as above, certain IPs only - ($parts[0] == 'shipment' && $parts[1] == 'dewars' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || - - # Allow formulatrix machines unauthorised access to inspections, certain IPs only - ($parts[0] == 'imaging' && $parts[1] == 'inspection' && in_array($_SERVER["REMOTE_ADDR"], $img)) || - - # Allow EPICS machines to create and close auto collect visits, certain IPs only. - # Permitted IPs listed in global variable $auto in config.php. - ($parts[0] == 'proposal' && $parts[1] == 'auto' && in_array($_SERVER["REMOTE_ADDR"], $auto)) - ) { - $need_auth = false; - } - - } - - if (sizeof($parts) > 0) { - if ($parts[0] == 'authenticate' || $parts[0] == 'options') $need_auth = false; - } - - # One time use tokens - $once = $this->app->request->get('token'); - if ($once) { - $token = $this->db->pq("SELECT o.validity, pe.personid, pe.login, CONCAT(p.proposalcode, p.proposalnumber) as prop - FROM SW_onceToken o - INNER JOIN proposal p ON p.proposalid = o.proposalid - INNER JOIN person pe ON pe.personid = o.personid - WHERE token=:1", array($once)); - if (sizeof($token)) { - $token = $token[0]; - $qs = $_SERVER['QUERY_STRING'] ? (preg_replace('/(&)?token=\w+/', '', str_replace('&', '&', $_SERVER['QUERY_STRING']))) : null; - if ($qs) $qs = '?'.$qs; - - if ($this->app->request->getResourceUri().$qs == $token['VALIDITY']) { - $_REQUEST['prop'] = $token['PROP']; - $this->user = $token['LOGIN']; - $need_auth = false; - $this->db->pq("DELETE FROM SW_onceToken WHERE token=:1", array($once)); - } - } else { - $this->_error(400, 'Invalid one time authorisation token'); - } - } - - # Remove tokens more than 10 seconds old, they should have been used - $this->db->pq("DELETE FROM SW_onceToken WHERE TIMESTAMPDIFF('SECOND', recordTimeStamp, CURRENT_TIMESTAMP) > 10"); - - if ($need_auth) $this->check_auth(); - } - - - - // Generate a new JWT encryption key - function generate_jwt_key() { - $this->_output(200, array('key' => base64_encode(openssl_random_pseudo_bytes(64)))); - } - - - // Generates a JWT token with the login embedded - function generate_jwt($login) { - global $jwt_key; - $key = base64_decode($jwt_key); - - $now = time(); - $data = array( - 'iat' => $now, - 'jti' => sha1($login . microtime(true)),//base64_encode(mcrypt_create_iv(32)), - 'iss' => 'http://'.$_SERVER['HTTP_HOST'], - 'nbf' => $now, - 'exp' => $now + 10 + 60*60*24, - 'data' => array( - 'login' => $login, - ) - ); - - $jwt = JWT::encode($data, $jwt_key, 'HS512'); - return array('jwt' => $jwt); - } - - - // Checks any supplied JWT is valid - function check_auth() { - global $jwt_key; - $key = base64_decode($jwt_key); - - // $auth_header = $this->app->request->headers->get('authorization'); - $headers = getallheaders(); - - if (array_key_exists('Authorization', $headers)) { - $auth_header = $headers['Authorization']; - } else $auth_header = ''; - - if ($auth_header) { - list($jwt) = sscanf($auth_header, 'Bearer %s'); - - try { - $token = JWT::decode($jwt, $jwt_key, array('HS512')); - $this->user = $token->data->login; - - // Invalid token - } catch (\Exception $e) { - $this->_error(401, 'Invalid authorisation token'); - } - - } else { - $this->_error(401, 'No authorisation token provided'); - } - } - - - - // Send correct for errors - function _output($code, $message) { - $this->app->response->setStatus($code); - - // Cant call $app->halt as app not yet running, just end process - header('X-PHP-Response-Code: '.$code, true, $code); - header('Content-Type: application/json'); - print json_encode($message); - exit(); - } - - function _error($code, $message) { - $this->_output($code, array('error' => $message)); - } - - - - // Calls the relevant Authentication Mechanism - function authenticate() - { - global $authentication_type; - - // urgh - $login = $this->app->request->post('login'); - $password = $this->app->request->post('password'); - - if (!$login) { - $bbreq = (array)json_decode($this->app->request()->getBody()); - $login = $bbreq['login']; - $password = $bbreq['password']; - } - - if (!$login) $this->_error(400, 'No login specified'); - if (!$password) $this->_error(400, 'No password specified'); - - $user = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($login)); - if (!sizeof($user)) { - $this->_error(400, 'Invalid Credentials'); - } - - if ($this->authenticateByType($authentication_type)->authenticate($login, $password)) { - $this->_output(200, $this->generate_jwt($login)); - } else { - $this->_error(400, 'Invalid Credentials'); - } - } - - // Logout - function logout() { - - } - - // Return instance of authentication class corresponding to $authentication_type. - // The value passed by the calling method derives from $authentication_type, a global variable specified in config.php. - - private function authenticateByType($authentication_type) - { - // Array of authentication types and corresponding authentication class names. - // Value is class name in SynchWeb\Authentication\Type namespace. - // Key is lower case representation of class name. - $authentication_types = array( - 'cas' => 'CAS', - 'dummy' => 'Dummy', - 'ldap' => 'LDAP', - 'simple' => 'Simple' - ); - - if (!$authentication_type) { - error_log("Authentication method not specified in config.php."); - - $authentication_type = 'CAS'; - } - - // Determine fully-qualified class name of authentication class corresponding to $authentication_type. - - $full_class_name = null; - - if (key_exists(strtolower($authentication_type), $authentication_types)) { - $full_class_name = 'SynchWeb\\Authentication\\Type\\' . $authentication_types[$authentication_type]; - } else { - error_log("Authentication method '$authentication_type' not configured."); - } - - // Return instance of authentication class. - - if (class_exists($full_class_name)) { - return new $full_class_name(); - } else { - error_log("Authentication class '$full_class_name' does not exist."); - } - - $this->_error(500, 'Authentication not possible due to a configuration error.'); - } -} diff --git a/api/src/Authentication/AuthenticationParent.php b/api/src/Authentication/AuthenticationParent.php index 3988a7da1..559051d10 100644 --- a/api/src/Authentication/AuthenticationParent.php +++ b/api/src/Authentication/AuthenticationParent.php @@ -9,7 +9,11 @@ class AuthenticationParent interface AuthenticationInterface { - public function authenticate($user, $pass); + public function authenticate($login, $password); public function check(); + + public function authorise(); + + public function authenticateByCode($code); } \ No newline at end of file diff --git a/api/src/Authentication/AuthenticationTypeFactory.php b/api/src/Authentication/AuthenticationTypeFactory.php new file mode 100644 index 000000000..67a3b7381 --- /dev/null +++ b/api/src/Authentication/AuthenticationTypeFactory.php @@ -0,0 +1,54 @@ + 'CAS', + 'dummy' => 'Dummy', + 'ldap' => 'LDAP', + 'simple' => 'Simple', + 'oidc' => 'OIDC', + 'combined' => 'Combined', + ); + + // Return instance of authentication class corresponding to $authentication_type. + // The value passed by the calling method derives from $authentication_type, a global variable specified in config.php. + public function create($authentication_type) + { + if (!$authentication_type) + { + error_log("Authentication method not specified in config.php."); + + $authentication_type = 'cas'; + } + + // Determine fully-qualified class name of authentication class corresponding to $authentication_type. + $full_class_name = null; + + if (key_exists(strtolower($authentication_type), $this->authentication_types)) + { + $full_class_name = 'SynchWeb\\Authentication\\Type\\' . $this->authentication_types[$authentication_type]; + } + else + { + throw new \Exception("Authentication method '$authentication_type' not configured."); + } + + // Return instance of authentication class. + if (class_exists($full_class_name)) + { + return new $full_class_name(); + } + else + { + throw new \Exception("Authentication class '$full_class_name' does not exist."); + } + + throw new \Exception('Authentication not possible due to a configuration error.'); + } +} \ No newline at end of file diff --git a/api/src/Authentication/Type/CAS.php b/api/src/Authentication/Type/CAS.php index 9d7bfed42..e7b24d2e7 100644 --- a/api/src/Authentication/Type/CAS.php +++ b/api/src/Authentication/Type/CAS.php @@ -8,12 +8,25 @@ class CAS extends AuthenticationParent implements AuthenticationInterface { + function authorise() + { + return false; + } + + function authenticateByCode($code) + { + return false; + } + function check() { global $cas_url, $cas_sso, $cacert; if (!$cas_sso) return false; + /** + * @psalm-suppress UndefinedConstant define in CAS.php + */ phpCAS::client(CAS_VERSION_2_0, $cas_url, 443, '/cas'); phpCAS::setCasServerCACert($cacert); diff --git a/api/src/Authentication/Type/Combined.php b/api/src/Authentication/Type/Combined.php new file mode 100644 index 000000000..7a0fe7386 --- /dev/null +++ b/api/src/Authentication/Type/Combined.php @@ -0,0 +1,39 @@ +CASAuth = new CAS(); + $this->OIDCAuth = new OIDC(); + } + + + public function authenticate($login, $password) + { + return $this->CASAuth->authenticate($login, $password); + } + + public function check() { + return $this->OIDCAuth->check(); + } + + public function authorise() { + return $this->OIDCAuth->authorise(); + } + + public function authenticateByCode($code){ + return $this->OIDCAuth->authenticateByCode($code); + } +} diff --git a/api/src/Authentication/Type/Dummy.php b/api/src/Authentication/Type/Dummy.php index 718f6bc8a..d5878e9bb 100644 --- a/api/src/Authentication/Type/Dummy.php +++ b/api/src/Authentication/Type/Dummy.php @@ -7,6 +7,16 @@ class Dummy extends AuthenticationParent implements AuthenticationInterface { + function authorise() + { + return false; + } + + function authenticateByCode($code) + { + return false; + } + function check() { return false; diff --git a/api/src/Authentication/Type/LDAP.php b/api/src/Authentication/Type/LDAP.php index 27dcbe097..669e92120 100644 --- a/api/src/Authentication/Type/LDAP.php +++ b/api/src/Authentication/Type/LDAP.php @@ -7,6 +7,16 @@ class LDAP extends AuthenticationParent implements AuthenticationInterface { + function authorise() + { + return false; + } + + function authenticateByCode($code) + { + return false; + } + function check() { return false; @@ -14,22 +24,44 @@ function check() function authenticate($login, $password) { - global $ldap_server; - global $ldap_search; + global $ldap_server, $ldap_search, $ldap_use_tls, $ldap_server_type, $active_directory_domain; + if (!$ldap_server_type) { + $ldap_search_type = "openldap"; + } $conn = ldap_connect($ldap_server); if ($conn) { // Tested against LDAP version 3 (could add support for older versions here) + /** + * @psalm-suppress UndefinedConstant + */ ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); + // use a secure connection for LDAP, if configured this way (default is unsecured as this was the historical setting) + if ($ldap_use_tls) { + ldap_start_tls($conn); + } try { - // testing with openldap indicates this call needs to use a correct - // DN syntax: "uid=,ou=people,dc=example,dc=com" - return ldap_bind($conn, "uid=" . $login . "," . $ldap_search, $password); + if ($ldap_server_type == "activedirectory") { + if (!$active_directory_domain) { + error_log("'active_directory_domain' parameter is not defined."); + error_log("\t This is required when LDAP server type is 'activedirectory'"); + return false; + } + $ldap_user = $active_directory_domain . "\\" . $login; + } else { + // testing with openldap indicates this call needs to use a correct + // DN syntax: "uid=,ou=people,dc=example,dc=com" + $ldap_user = "uid=" . $login . "," . $ldap_search; + } + return ldap_bind($conn, $ldap_user, $password); // Couldn't bind } catch (\Exception $e) { + error_log("SynchWeb - LDAP Auth FAILURE for user $login"); + error_log("\t" . $e->getMessage()); + error_log("\tldap_error: " . ldap_error($conn) . " (Err Code: " . ldap_errno($conn) . ")"); return false; } } diff --git a/api/src/Authentication/Type/OIDC.php b/api/src/Authentication/Type/OIDC.php new file mode 100644 index 000000000..c8971f488 --- /dev/null +++ b/api/src/Authentication/Type/OIDC.php @@ -0,0 +1,129 @@ +providerConfigCache)) { + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'https://' . $sso_url . '/.well-known/openid-configuration'); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + $newProviderConfig = json_decode($response); + + if(!$newProviderConfig + || !isset($newProviderConfig->userinfo_endpoint) + || !isset($newProviderConfig->authorization_endpoint) + || !isset($newProviderConfig->token_endpoint)) { + error_log("OIDC Authentication provider replied with invalid JSON body"); + return null; + } + $newProviderConfig->b64ClientCreds = base64_encode( + $oidc_client_id . ":" . $oidc_client_secret + ); + + $this->providerConfigCache = $newProviderConfig; + } + return $this->providerConfigCache; + } + + private function getUser($token) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->getProviderConfig()->userinfo_endpoint); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $token)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + + $response_json = json_decode($response); + if (!$response_json || !isset($response_json->id)) { + return false; + } + + return $response_json->id; + } + + function authenticate($login, $password) + { + return false; + } + + function check() + { + global $cookie_key; + + if (array_key_exists($cookie_key, $_COOKIE)) { + return($this->getUser($_COOKIE[$cookie_key])); + } + + return false; + } + + function authorise() + { + global $oidc_client_id; + $redirect_url = Utils::filterParamFromUrl($_SERVER["HTTP_REFERER"], "code"); + + return ( $this->getProviderConfig()->authorization_endpoint . + '?response_type=code&client_id=' . $oidc_client_id . + '&redirect_uri=' . $redirect_url + ); + } + + function authenticateByCode($code) + { + global $cacert, $oidc_client_secret, $oidc_client_id, $cookie_key; + + $redirect_url = Utils::filterParamFromUrl($_SERVER["HTTP_REFERER"], "code"); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $this->getProviderConfig()->token_endpoint . + '?grant_type=authorization_code&redirect_uri=' . + $redirect_url . + "&code=" . $code + ); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Basic ' . $this->getProviderConfig()->b64ClientCreds)); + $response = curl_exec($ch); + curl_close($ch); + + $response_json = json_decode($response); + if (!$response_json || !isset($response_json->access_token)) { + error_log("Invalid authentication attempt, provider returned invalid response"); + return false; + } + + $token = $response_json->access_token; + + if(!$token) { + error_log("Invalid authentication attempt, provider returned no access token"); + return false; + } + + $cookieOpts = array ( + 'expires' => time() + $response_json->expires_in, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => 'Strict' + ); + + setcookie($cookie_key, $token, $cookieOpts); + return $this->getUser($token); + } +} diff --git a/api/src/Authentication/Type/Simple.php b/api/src/Authentication/Type/Simple.php index 88c3ac3e7..6a355946c 100644 --- a/api/src/Authentication/Type/Simple.php +++ b/api/src/Authentication/Type/Simple.php @@ -7,6 +7,16 @@ class Simple extends AuthenticationParent implements AuthenticationInterface { + function authorise() + { + return false; + } + + function authenticateByCode($code) + { + return false; + } + function check() { return false; diff --git a/api/src/Controllers/AssignController.php b/api/src/Controllers/AssignController.php new file mode 100644 index 000000000..30bcbc79a --- /dev/null +++ b/api/src/Controllers/AssignController.php @@ -0,0 +1,149 @@ + '\w+\d+-\d+', 'cid' => '\d+', 'did' => '\d+', 'pos' => '\d+', 'bl' => '[\w\-]+'); + + public static $dispatch = array(array('/visits(/:visit)', 'get', 'getBeamlineVisits'), + array('/assign', 'get', 'assignContainer'), + array('/unassign', 'get', 'unassignContainer'), + array('/deact', 'get', 'deactivateDewar'), + array('/names', 'get', 'getPuckNames'), + + ); + + function __construct(Slim $app, $db, $user, AssignData $assignData) + { + // Call parent constructor to register our routes + parent::__construct($app, $db, $user); + $this->app = $app; + $this->assignData = $assignData; + } + + function assignContainer() + { + $cs = $this->assignData->getContainer($this->arg('visit'), $this->arg('cid')); + + if (sizeof($cs) > 0) + { + $this->assignData->assignContainer($cs[0], $this->arg('pos')); + $this->_output(1); + } + else + { + $this->_output(0); + } + } + + function unassignContainer() + { + $cs = $this->assignData->getContainer($this->arg('visit'), $this->arg('cid')); + + if (sizeof($cs) > 0) + { + $this->assignData->unassignContainer($cs[0]); + $this->_output(1); + } + else + { + $this->_output(0); + } + } + + function deactivateDewar() + { + $ds = $this->assignData->getDewar($this->arg('did'), $this->proposalid, $this->def_arg('visit', null)); + + if (sizeof($ds) > 0) + { + $this->assignData->deactivateDewar($this->arg('did')); + $this->_output(1); + } + else + { + $this->_output(0); + } + } + + + # ------------------------------------------------------------------------ + # Return visits for beamline + function getBeamlineVisits($visit = null) + { + $visits = $this->blsr_visits(); + + if ($visit) + { + foreach ($visits as $i => $v) + { + if ($v['VISIT'] == $visit) + { + $this->_output($v); + return; + } + } + $this->_error('No such visit'); + } + else + $this->_output($visits); + } + + + # ------------------------------------------------------------------------ + # Puck names from puck scanner + # BL03I-MO-ROBOT-01:PUCK_01_NAME + function getPuckNames() + { + global $bl_pv_map; + session_write_close(); + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + if (!$this->has_arg('bl')) + $this->_error('No beamline specified'); + if (!array_key_exists($this->arg('bl'), $bl_pv_map)) + $this->_error('No such beamline'); + $pv_prefix = $bl_pv_map[$this->arg('bl')]; + + $pvs = array(); + for ($i = 1; $i < 38; $i++) + { + $id = $i < 10 ? '0' . $i : $i; + array_push($pvs, $pv_prefix . '-MO-ROBOT-01:PUCK_' . $id . '_NAME'); + } + + $rows = $this->assignData->getContainerBarcodesForProposal($this->proposalid); + $codes = array(); + foreach ($rows as $r) + { + array_push($codes, rtrim($r['BARCODE'])); + } + + $return = array(); + $vals = $this->pv(array_values($pvs), true, true); + foreach ($vals as $k => $v) + { + if (preg_match('/PUCK_(\d+)_NAME/', $k, $mat)) + { + if (is_array($v) && sizeof($v)) + { + $val = (!in_array($v[0], $codes) && !$this->staff) ? '[Loaded]' : $v[0]; + } + else + $val = ''; + array_push($return, array('id' => intval($mat[1]), 'name' => $val)); + } + } + + $this->_output($return); + } +} \ No newline at end of file diff --git a/api/src/Controllers/AuthenticationController.php b/api/src/Controllers/AuthenticationController.php new file mode 100644 index 000000000..15696a154 --- /dev/null +++ b/api/src/Controllers/AuthenticationController.php @@ -0,0 +1,391 @@ +app = $app; + $this->dataLayer = $dataLayer; + $this->exitOnError = $exitOnError; + $this->authenticationTypeFactory = $authenticationTypeFactory ?: new AuthenticationTypeFactory(); + + $this->setupRoutes(); + } + + private function setupRoutes() + { + $this->app->post('/authenticate', array(&$this, 'authenticate'))->conditions(array( + 'login' => '[A-z0-9]+', + 'password' => '.*', + )); + + $this->app->post('/authenticate/token', array(&$this, 'authenticateByCode'))->conditions(array( + 'code' => '[A-z0-9\-]+', + )); + + $this->app->get('/authenticate/check', array(&$this, 'check')); + $this->app->get('/authenticate/key', array(&$this, 'returnResponseWithJwtKey')); + $this->app->get('/authenticate/logout', array(&$this, 'logout')); + $this->app->get('/authenticate/authorise', array(&$this, 'authorise')); + } + + function getUser(): User + { + if (!$this->loginId) + { + $this->validateAuthentication(); + } + if ($this->loginId) + { + return $this->dataLayer->getUser($this->loginId); + } + + // This is a user who is logging in from a machine specified by remote IP + return new User($this->loginId, 0, "Machine", "", [], []); + } + + // For SSO check if we are already logged in elsewhere + // - if a mechanism exists to do this + function check() + { + $userId = $this->authenticateByType()->check(); + + if ($userId) + { + if ($this->dataLayer->isUserLoggedIn($userId)) + { + $this->returnResponse(200, $this->generateJwtToken($userId)); + } + } + $this->returnError(400, 'No previous session'); + } + + private function checkAuthRequiredForSpecificSituations($parts): bool + { + global $blsr, $bcr, $img, $auto; + + $need_auth = true; + # Work around to allow beamline sample registration without CAS authentication + if (sizeof($parts) >= 3) + { + if ( + # Calendar ICS export + ($parts[0] == 'cal' && $parts[1] == 'ics' && $parts[2] == 'h') || + + # Allow formulatrix machines unauthorised access to inspections, certain IPs only + ($parts[0] == 'imaging' && $parts[1] == 'inspection' && in_array($_SERVER["REMOTE_ADDR"], $img)) || + + # For use on the touchscreen computers in the hutch. + # Handles api calls: /assign/visits/ e.g./assign/visits/mx1234-1 + ($parts[0] == 'assign' && $parts[1] == 'visits' && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || + + # Allow barcode reader ips unauthorised access to add history + ($parts[0] == 'shipment' && $parts[1] == 'dewars' && $parts[2] == 'history' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || + + # Allow barcode reader ips unauthorised access to add comments + ($parts[0] == 'shipment' && $parts[1] == 'dewars' && $parts[2] == 'comments' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || + + # Container notification: allow beamlines running in automated mode to notify users + ($parts[0] == 'shipment' && $parts[1] == 'containers' && $parts[2] == 'notify' && in_array($_SERVER["REMOTE_ADDR"], $auto)) || + + # Allow barcode reader ips unauthorised access to add container history + ($parts[0] == 'shipment' && $parts[1] == 'containers' && $parts[2] == 'history' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) + ) + { + $need_auth = false; + } + + } + else if (sizeof($parts) >= 2) + { + if ( + # For use on the touchscreen computers in the hutch + # Handles api calls: /assign/assign, /assign/unassign, /assign/deact, /assign/visits + (($parts[0] == 'assign') && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || + (($parts[0] == 'shipment' && $parts[1] == 'containers') && in_array($_SERVER["REMOTE_ADDR"], $blsr)) || + + # Allow barcode reader unauthorised access, same as above, certain IPs only + ($parts[0] == 'shipment' && $parts[1] == 'dewars' && in_array($_SERVER["REMOTE_ADDR"], $bcr)) || + + # Allow formulatrix machines unauthorised access to inspections, certain IPs only + ($parts[0] == 'imaging' && $parts[1] == 'inspection' && in_array($_SERVER["REMOTE_ADDR"], $img)) || + + # Allow EPICS machines to create and close auto collect visits, certain IPs only. + # Permitted IPs listed in global variable $auto in config.php. + ($parts[0] == 'proposal' && $parts[1] == 'auto' && in_array($_SERVER["REMOTE_ADDR"], $auto)) + ) + { + $need_auth = false; + } + + } + + if ($need_auth && sizeof($parts) > 0) + { + if ($parts[0] == 'authenticate' || $parts[0] == 'options') + $need_auth = false; + } + return $need_auth; + } + + private function processOneTimeUseTokens(): bool + { + $need_auth = true; + $tokenId = $this->app->request()->get('token'); + if ($tokenId) + { + # Remove tokens more than 10 seconds old, they should have been used + $this->dataLayer->deleteOldOneTimeUseTokens(); + $token = $this->dataLayer->getOneTimeUseToken($tokenId); + if (sizeof($token)) + { + $token = $token[0]; + $qs = $_SERVER['QUERY_STRING'] ? (preg_replace('/(&)?token=\w+/', '', str_replace('&', '&', $_SERVER['QUERY_STRING']))) : null; + if ($qs) + $qs = '?' . $qs; + + if ($this->app->request->getResourceUri() . $qs == $token['VALIDITY']) + { + $_REQUEST['prop'] = $token['PROP']; + $this->loginId = $token['LOGIN']; + $need_auth = false; + $this->dataLayer->deleteOneTimeUseToken($tokenId); + } + } + else + { + $this->returnError(400, 'Invalid one time authorisation token'); + } + } + + return $need_auth; + } + + private function getUrlParameters() + { + $parts = explode('/', $this->app->request()->getResourceUri()); + if (sizeof($parts)) + { + array_shift($parts); // drop the first part of the url - i.e. the domain name + } + return $parts; + } + + // Check if this request needs authentication + // We allow some pages unauthorised access based on IP, or calendar hash + function validateAuthentication() + { + $need_auth = $this->processOneTimeUseTokens(); + if ($need_auth) + { + $parts = $this->getUrlParameters(); + $need_auth = $this->checkAuthRequiredForSpecificSituations($parts); + } + if ($need_auth) + { + $this->checkForAndValidateAuthenticationToken(); + } + } + + function updateActivityTimestamp() + { + if ($this->loginId) + { + $this->dataLayer->updateActivityTimestamp($this->loginId); + } + } + + // Generate a new JWT encryption key + function returnResponseWithJwtKey() + { + $this->returnResponse(200, array('key' => base64_encode(openssl_random_pseudo_bytes(64)))); + } + + // Generates a JWT token with the login embedded + private function generateJwtToken($login) + { + global $jwt_key; + + $now = time(); + $data = array( + 'iat' => $now, + 'jti' => sha1($login . microtime(true)), //base64_encode(mcrypt_create_iv(32)), + 'iss' => 'http://' . $_SERVER['HTTP_HOST'], + 'nbf' => $now, + 'exp' => $now + 10 + 60 * 60 * 24, + 'data' => array( + 'login' => $login, + ) + ); + + $jwt = JWT::encode($data, $jwt_key, 'HS512'); + return array('jwt' => $jwt); + } + + private function checkForAndValidateAuthenticationToken() + { + global $jwt_key; + + $headers = getallheaders(); + $auth_header = ''; + if (array_key_exists('Authorization', $headers)) + { + $auth_header = $headers['Authorization']; + } + + if ($auth_header) + { + list($jwt) = sscanf($auth_header, 'Bearer %s'); + + try + { + $token = JWT::decode($jwt, $jwt_key, array('HS512')); + $this->loginId = $token->data->login; + } + catch (\Exception $e) + { + $this->returnError(401, 'Invalid authorisation token'); + } + + } + else + { + $this->returnError(401, 'No authorisation token provided'); + } + } + + // Send correct for errors + private function returnResponse($code, $message) + { + $this->app->response()->setStatus($code); + + // Can't call $app->halt as app not yet running, just end process + header('X-PHP-Response-Code: ' . $code, true, $code); + header('Content-Type: application/json'); + print json_encode($message); + if ($this->exitOnError) + { + exit(); + } else { + throw new \Exception(self::ErrorUnderTestExceptionMessage); + } + } + + private function returnError($code, $message) + { + $this->returnResponse($code, array('error' => $message)); + } + + // Calls the relevant Authentication Mechanism + function authenticate() + { + // urgh + $login = $this->app->request->post('login'); + $password = $this->app->request->post('password'); + + if (!$login) + { + $bbreq = (array) json_decode($this->app->request()->getBody()); + $login = $bbreq['login']; + $password = $bbreq['password']; + } + + if (!$login) + $this->returnError(400, 'No login specified'); + if (!$password) + $this->returnError(400, 'No password specified'); + + if (!$this->dataLayer->isUserLoggedIn($login)) + { + $this->returnError(400, 'Invalid Credentials'); + } + + if ($this->authenticateByType()->authenticate($login, $password)) + { + $this->returnResponse(200, $this->generateJwtToken($login)); + } + else + { + $this->returnError(400, 'Invalid Credentials'); + } + } + + function authorise() + { + global $cas_sso; + + if ($cas_sso) { + header('Location: ' . $this->authenticateByType()->authorise()); + $this->returnResponse(302, array('status' => "Redirecting to CAS")); + } else { + $this->returnError(501, "SSO not configured"); + } + } + + function authenticateByCode() + { + $code = $this->app->request()->post('code'); + $fedid = $this->authenticateByType()->authenticateByCode($code); + if ($fedid) { + $this->returnResponse(200, $this->generateJwtToken($fedid)); + } else { + $this->returnError(401, 'Invalid Credentials'); + } + } + + // Logout + function logout() + { + global $cookie_key; + if (isset($_COOKIE[$cookie_key])) { + $cookieOpts = array ( + 'expires' => time() - 3600, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => 'Strict' + ); + + setcookie($cookie_key, null, $cookieOpts); + } + } + + private function authenticateByType() { + global $authentication_type; + try { + return $this->authenticationTypeFactory->create($authentication_type); + } + catch(\Exception $e) { + error_log($e->getMessage()); + $this->returnError(500, 'Invalid Authentication Config'); + } + } + +} \ No newline at end of file diff --git a/api/src/Controllers/UserController.php b/api/src/Controllers/UserController.php new file mode 100644 index 000000000..9951a9d6e --- /dev/null +++ b/api/src/Controllers/UserController.php @@ -0,0 +1,365 @@ + '\d+', + 'pid' => '\d+', + 'pjid' => '\d+', + 'peid' => '\d+', + 'uid' => '\d+', + 'sid' => '\d+', + 'visit' => '\w+\d+-\d+', + 'location' => '(\w|-|\/)+', + 'all' => '\d', + 'login' => '\d', + + 'NAME' => '\w+', + + 'TYPE' => '\w+', + 'DESCRIPTION' => '(\w|\s)+', + + + 'PERSONID' => '\d+', + 'FAMILYNAME' => '([\w\-])+', + 'GIVENNAME' => '([\w\-])+', + 'PHONENUMBER' => '.*', + 'EMAILADDRESS' => '.*', + 'LABNAME' => '([\w\s\-])+', + 'ADDRESS' => '([\w\s\-\n])+', + 'COUNTRY' => '([\w\s\-])+', + 'CITY' => '([\w\s\-])+', + 'POSTCODE' => '([\w\s\-])+', + 'LOGIN' => '\w+', + 'PASSWORD' => '.*', + ); + + public static $dispatch = array(array('(/:PERSONID)', 'get', '_get_users'), + array('/:PERSONID', 'patch', '_update_user'), + array('/', 'post', '_add_user'), + + array('/current', 'get', 'getCurrentUser'), + + array('/login', 'get', '_login'), + array('/log(/)', 'post', '_log_action'), + array('/time', 'get', 'getTime'), + + array('/groups(/:gid)', 'get', '_get_groups'), + array('/groups', 'post', '_add_group'), + array('/groups/:gid', 'put', '_update_group'), + + array('/groups/:gid/permission/:pid', 'post', '_add_group_permission'), + array('/groups/:gid/permission/:pid', 'delete', '_remove_group_permission'), + + array('/groups/:gid/users/:peid', 'post', '_add_group_user'), + array('/groups/:gid/users/:peid', 'delete', '_remove_group_user'), + + array('/permissions(/:pid)(/group/:gid)', 'get', '_get_permissions'), + array('/permissions', 'post', '_add_permission'), + array('/permissions/:pid', 'put', '_update_permission'), + ); + + function __construct(Slim $app, $db, $user, UserData $userData) + { + // Call parent constructor to register our routes + parent::__construct($app, $db, $user); + $this->app = $app; + $this->userData = $userData; + } + + # ------------------------------------------------------------------------ + # Helpers for backbone application + function getCurrentUser() + { + $this->_output(array( + 'personid' => $this->user->personId, + 'user' => $this->user->loginId, + 'givenname' => $this->user->givenName, + 'permissions' => $this->user->perms, + 'is_staff' => $this->staff, + 'visits' => $this->visits, + 'ty' => $this->ty) + ); + } + + function _login() + { + } + + function _log_action() + { + if (!$this->has_arg('location')) + $this->_error('No location specified'); + $this->log_action(1, $this->arg('location')); + print $this->arg('location'); + } + + # ------------------------------------------------------------------------ + # Get current time + function getTime() + { + $d = new \DateTime("now"); + $this->_output(array('TIME' => $d->format('D M d Y H:i:s (\G\M\TO)'))); + } + + function _get_groups() + { + $this->haltIfLackingPermission('manage_groups'); + + $gid = $this->def_arg('gid', null); + $groups = $this->userData->getGroups($gid); + + if ($gid) + { + if (sizeof($groups)) + $this->_output($groups[0]); + else + $this->_error('No such group'); + } + else + { + $this->_output($groups); + } + } + + function _add_group() + { + $this->haltIfLackingPermission('manage_groups'); + + if (!$this->has_arg('NAME')) + { + $this->_error('No group name'); + } + else + { + $this->_output(array('USERGROUPID' => $this->userData->addGroup($this->arg('NAME')))); + } + } + + function _update_group() + { + $this->haltIfLackingPermission('manage_groups'); + $this->userData->updateGroup($this->arg('gid'), $this->arg('NAME')); + $this->_output(array('NAME' => $this->arg('NAME'))); + } + + function _add_group_permission() + { + $this->haltIfLackingPermission('manage_groups'); + $this->userData->addGroupPermission($this->arg('gid'), $this->arg('pid')); + $this->_output(array('USERGROUPID' => $this->arg('gid'), 'PERMISSIONID' => $this->arg('pid'))); + } + + function _remove_group_permission() + { + $this->haltIfLackingPermission('manage_groups'); + $this->userData->removeGroupPermission($this->arg('gid'), $this->arg('pid')); + $this->_output(1); + } + + function _get_users() + { + // remove this after successfully completing LIMS-742 ticket + if($this->has_arg('pid')){ + error_log("pid used (for ticket: https://jira.diamond.ac.uk/browse/LIMS-742)"); + } + + $rows = $this->userData->getUsers( + false, + $this->staff, + $this->argOrEmptyString('s'), + $this->argOrEmptyString('page'), + $this->argOrEmptyString('sort_by'), + $this->argOrEmptyString('pid'), + $this->proposalid, + $this->argOrEmptyString('PERSONID'), + $this->user->hasPermission('manage_users'), + $this->user->personId, + $this->argOrEmptyString('gid'), + $this->argOrEmptyString('sid'), + $this->argOrEmptyString('pjid'), + $this->argOrEmptyString('visit'), + $this->def_arg('per_page', 15), + $this->def_arg('order', 'asc') == 'asc', + $this->has_arg('all'), + $this->has_arg('login') + ); + + if ($this->has_arg('PERSONID')) + { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such user'); + } + else + { + $tot = $this->userData->getUsers( + true, + $this->staff, + $this->argOrEmptyString('s'), + $this->argOrEmptyString('page'), + $this->argOrEmptyString('sort_by'), + $this->argOrEmptyString('pid'), + $this->proposalid, + $this->argOrEmptyString('PERSONID'), + $this->user->hasPermission('manage_users'), + $this->user->personId, + $this->argOrEmptyString('gid'), + $this->argOrEmptyString('sid'), + $this->argOrEmptyString('pjid'), + $this->argOrEmptyString('visit'), + $this->def_arg('per_page', 15), + $this->def_arg('order', 'asc') == 'asc', + $this->has_arg('all'), + $this->has_arg('login') + ); + $this->_output(array('total' => $tot, + 'data' => $rows, + )); + } + } + + function _check_login() + { + $this->haltIfLackingPermission('manage_users'); + $person = $this->userData->checkLogin($this->arg('LOGIN')); + + if (!sizeof($person)) + $this->_error('Login not used'); + else + $this->_output(new \stdClass); + } + + function _add_user() + { + $this->haltIfLackingPermission('manage_users'); + $personId = $this->userData->addUser( + $this->arg('LOGIN'), + $this->arg('GIVENNAME'), + $this->arg('FAMILYNAME'), + $this->def_arg('EMAILADDRESS', null) + ); + $this->_output(array('PERSONID' => $personId)); + } + + function _update_user() + { + // TODO: should this require 'manage_users'? + if (!$this->has_arg('PERSONID')) + $this->_error('No person specified'); + + $person = $this->userData->getUser($this->user->personId, $this->proposalid, $this->arg('PERSONID')); + + if (!sizeof($person)) + $this->_error('No such person'); + + $person = $person[0]; + + $this->userData->updateUser( + $this->arg('PERSONID'), + $this->argOrNull('FAMILYNAME'), + $this->argOrNull('GIVENNAME'), + $this->argOrNull('PHONENUMBER'), + $this->argOrNull('EMAILADDRESS') + ); + + $person = $this->userData->getUser($this->user->personId, $this->proposalid, $this->arg('PERSONID')); + $person = $person[0]; + $this->_output((array) $person); + $laboratory = null; + if ($person['LABORATORYID']) + { + $laboratory = $this->userData->getLaboratory($person['LABORATORYID'])[0]; + } + + $this->userData->updateLaboratory( + $this->arg('PERSONID'), + $this->argOrNull('LABNAME'), + $this->argOrNull('ADDRESS'), + $this->argOrNull('CITY'), + $this->argOrNull('POSTCODE'), + $this->argOrNull('COUNTRY'), + $person['LABORATORYID'] + ); + $laboratory = $this->userData->getLaboratory($person['LABORATORYID']); + $this->_output((array) $laboratory[0]); + } + + function _add_group_user() + { + $this->haltIfLackingPermission('manage_groups'); + $this->userData->addGroupUser($this->arg('peid'), $this->arg('gid')); + $this->_output(array('USERGROUPID' => $this->arg('gid'), 'PERSONID' => $this->arg('peid'))); + } + + function _remove_group_user() + { + $this->haltIfLackingPermission('manage_groups'); + $this->userData->removeGroupUser($this->arg('peid'), $this->arg('gid')); + $this->_output(1); + } + + function _get_permissions() + { + $this->haltIfLackingPermission('manage_perms'); + + $rows = $this->userData->getPermissions( + false, + $this->argOrEmptyString('s'), + $this->argOrEmptyString('gid'), + $this->argOrEmptyString('pid'), + $this->def_arg('per_page', 15), + $this->def_arg('page', 0) + ); + + if ($this->has_arg('pid')) + { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such permission'); + } + else + { + $tot = $this->userData->getPermissions( + true, + $this->argOrEmptyString('s'), + $this->argOrEmptyString('gid'), + $this->argOrEmptyString('pid'), + $this->def_arg('per_page', 15), + $this->def_arg('page', 0) + ); + $this->_output(array('total' => $tot, + 'data' => $rows, + )); + } + } + + + function _add_permission() + { + $this->haltIfLackingPermission('manage_perms'); + $this->_output(array('PERMISSIONID' => $this->userData->addPermission($this->arg('TYPE'), $this->argOrEmptyString('DESCRIPTION')))); + } + + function _update_permission() + { + $this->haltIfLackingPermission('manage_perms'); + $desc = $this->argOrEmptyString('DESCRIPTION'); + $this->userData->updatePermission( + $this->arg('pid'), + $this->arg('TYPE'), + $desc + ); + $this->_output(array('TYPE' => $this->arg('TYPE'), 'DESCRIPTION' => $desc)); + } +} \ No newline at end of file diff --git a/api/src/Database.php b/api/src/Database.php deleted file mode 100644 index 30db65474..000000000 --- a/api/src/Database.php +++ /dev/null @@ -1,49 +0,0 @@ - 'MySQL' - ); - - if (!$database_type) { - error_log('Database type ($dbtype) is not specified in config.php.'); - - $database_type = 'MySQL'; - } - - // Determine fully-qualified class name of database class corresponding to $database_type. - - $full_class_name = null; - - if (key_exists(strtolower($database_type), $database_types)) { - $full_class_name = 'SynchWeb\\Database\\Type\\' . $database_types[$database_type]; - } else { - error_log("Database type '$database_type' not configured."); - } - - // Return instance of database class. - - if (class_exists($full_class_name)) { - $port = array_key_exists('port', $isb) ? $isb['port'] : null; - - return new $full_class_name($isb['user'], $isb['pass'], $isb['db'], $port); - } else { - error_log("Database class '$full_class_name' does not exist."); - } - - // $this->_error(500, 'Database connection not possible due to a configuration error.'); - } -} diff --git a/api/src/Database/DatabaseConnectionFactory.php b/api/src/Database/DatabaseConnectionFactory.php new file mode 100644 index 000000000..0afb84363 --- /dev/null +++ b/api/src/Database/DatabaseConnectionFactory.php @@ -0,0 +1,44 @@ +set_charset("utf8mb4"); + } + elseif ($databaseType == 'PureMySQL') { + $port = array_key_exists('port', $summarydbconfig) ? $summarydbconfig['port'] : null; + if (!$port) { + $port = ini_get("mysqli.default_port"); + } + list($host, $dbn) = explode('/', $summarydbconfig['db']); + $conn = new \mysqli($host, $summarydbconfig['user'], $summarydbconfig['pass'], $dbn, $port); + $conn->set_charset("utf8mb4"); + } + + + if ($conn == null) { + Utils::returnError("Database Configuration Error", "Database connection for type '$databaseType' does not exist."); + } + return $conn; + } +} \ No newline at end of file diff --git a/api/src/Database/DatabaseFactory.php b/api/src/Database/DatabaseFactory.php new file mode 100644 index 000000000..61ba33c04 --- /dev/null +++ b/api/src/Database/DatabaseFactory.php @@ -0,0 +1,57 @@ + ["dbClassName" =>'MySQL', "dataConnectionName" => 'MySQL'], + 'summary' => ["dbClassName" =>'PureMySQL', "dataConnectionName" => 'PureMySQL'] + ); + + function __construct($databaseConnectionFactory) + { + $this->databaseConnectionFactory = $databaseConnectionFactory; + } + + private $databaseConnectionFactory; + + public function get($databaseType = null) + { + global $dbtype; + + if ( $databaseType == null) { + // Global variable is named $dbtype in config.php. + $databaseType = $dbtype; + } + + if (!$databaseType) { + error_log('Database type variable, dbtype, is not specified in config.php - defaulting to MySql.'); + $databaseType = 'MySQL'; + } + + // Determine fully-qualified class name of database class corresponding to $database_type. + if (key_exists(strtolower($databaseType), $this->database_types)) { + $selectedDBConfig = $this->database_types[strtolower($databaseType)]; + + $full_class_name = 'SynchWeb\\Database\\Type\\' . $selectedDBConfig["dbClassName"]; + + if (class_exists($full_class_name)) { + $conn = $this->databaseConnectionFactory->get($selectedDBConfig["dataConnectionName"]); + return new $full_class_name($conn); + } + else { + error_log("Database class '$full_class_name' does not exist."); + } + + } + else { + error_log("Database type '$databaseType' not configured."); + } + return null; + } +} \ No newline at end of file diff --git a/api/src/Database/DatabaseParent.php b/api/src/Database/DatabaseParent.php index ef604bd42..37de3f83a 100644 --- a/api/src/Database/DatabaseParent.php +++ b/api/src/Database/DatabaseParent.php @@ -2,20 +2,81 @@ namespace SynchWeb\Database; -class DatabaseParent +use SynchWeb\Utils; +use Slim\Slim; + +interface DatabaseInterface +{ + // Prepared Query + public function pq($query, $args = array()); + + // Paginated Query + public function paginate($query, $args); + + // Union multiple queries that take the same arguments and return the same columns + // Query can optionally be wrapped, where :QUERY will be replaced with the inner query: + // $wrapper = 'SELECT * FROM (:QUERY) GROUP BY id'; + public function union($queries, $args, $all = false, $wrapper = null); + + // Read binary + public function read($field); + + // Return last insert id + public function id(); + + // Close connection + public function close(); + + // Start a transaction + function start_transaction(); + + // End a transaction + function end_transaction(); + + // Use to wait for any replication synchronisation in the db cluster + // For non-cluster databases no-op + function wait_rep_sync($state = false); +} + +abstract class DatabaseParent implements DatabaseInterface { + /** + * Setting to true produces a text of the database call + * @var bool + */ public $debug = False; + + /** Setting to true should produce statistics output in $stat, if possible in the driver + * @var bool + */ public $stats = False; - public $stat; - private $app; + public $stat = ""; + + /** + * Setting to true should produce the explain plan in $plan, if possible in the driver + * @var bool + */ + public $explain = False; + public $plan = ""; + + protected $conn; + /* @var string */ + protected $type = "Base"; // The sub-class should override this + + /* @var ?Slim */ + private $app = NULL; + + public function __construct($conn) + { + $this->conn = $conn; + } function type() { return $this->type; } - public function set_app($app) - { + public function set_app(Slim $app) { $this->app = $app; } @@ -26,42 +87,23 @@ public function set_stats($st) public function set_debug($debug) { - if ($this->app) $this->app->contentType('text/html'); + if ($this->app) + $this->app->contentType('text/html'); $this->debug = $debug; } - public function error($title, $msg) + function set_explain(bool $explain) { - header('HTTP/1.1 503 Service Unavailable'); - // header('Content-type:application/json'); - print json_encode(array('title' => $title, 'msg' => $msg)); - error_log('Database Error: ' . $msg); - exit(); + $this->$explain = $explain; } + public function error($title, $msg) + { + Utils::returnError($title, $msg); + } function __destruct() { - // $this->close(); + // $this->close(); } } - -interface DatabaseInterface -{ - public function __construct($user, $pass, $db, $port); - - // Prepared Query - public function pq($query, $arguments); - - // Paginated Query - public function paginate($query, $arguments); - - // Read binary - public function read($field); - - // Return last insert id - public function id(); - - // Close connection - public function close(); -} diff --git a/api/src/Database/DatabaseQueryBuilder.php b/api/src/Database/DatabaseQueryBuilder.php new file mode 100644 index 000000000..1a41ba920 --- /dev/null +++ b/api/src/Database/DatabaseQueryBuilder.php @@ -0,0 +1,167 @@ +patch("FAMILYNAME", $familyName) + * ->patch("GIVENNAME", $givenName) + * ->where("personid", $personId) + * ->update("person"); + * runs $db->pq(UPDATE person SET FAMILYNAME=:1, GIVENNAME=:2 WHERE personid=:3, array($familyName, $givenName, $personid)) + */ +class DatabaseQueryBuilder +{ + /** @var array values in update statement*/ + private $update_values = []; + /** @var array bound values*/ + private $query_bound_values = []; + /** @var ?string where clause column name */ + private $where_columnName = Null; + /** @var ?mixed where clause value */ + private $where_value = Null; + /** @var array join clauses*/ + private $join_clauses = []; + + /** + * @var DatabaseParent database parent to use + */ + private $db; + + public function __construct(DatabaseParent $db) + { + $this->db = $db; + } + + /** + * Patch paramters, values to set in an update but they are only added if they are not null + * to enable records to be patched. + * + * @param string $columnName the db column name + * @param mixed $value the value to set it to + * @return DatabaseQueryBuilder this to make a fluent interface + */ + public function patch($columnName, $value) + { + if (is_null($value)) + { + return $this; + } + $this->update_values[$columnName] = $value; + + return $this; + } + + /** + * Add a where caluse based on an id + * + * @param string $columnName The column name of the identity + * @param mixed $value The value for the identity, i.e. which return to update + * @return DatabaseQueryBuilder this to make a fluent interface + */ + public function whereIdEquals($columnName, $value) + { + $this->where_columnName = $columnName; + $this->where_value = $value; + return $this; + } + + /** + * Run the update if any patch parameters have been set + * + * @param string $expectedTable Table name + */ + public function update($expectedTable) + { + + if (sizeof($this->update_values) < 1) + return null; + $set = $this->bindArrayAsList($this->update_values); + + $where = $this->where_columnName . "=:" . $this->addBoundVariable($this->where_value); + + return $this->db->pq("UPDATE {$expectedTable} SET {$set} WHERE {$where}", $this->query_bound_values); + } + + /** + * Run the insert if any patch parameters have been set + * + * @param string $expectedTable Table name + * @return ?int|string The auto increment number as a number unless it is greater than int in which case a string. + * 0 if no auto-increment column + */ + public function insert($expectedTable) + { + + if (sizeof($this->update_values) < 1) + return null; + $values = $this->bindArrayAsInsertValues($this->update_values); + + $this->db->pq("INSERT INTO {$expectedTable} ( {$values["names"]} ) VALUES ( ${values["binds"]} )", $this->query_bound_values); + return $this->db->id(); + } + + + /** + * Add while join clauses with no processing except to remove duplicates + * ONLY USED WITH getJoins at the moment + * + * @param string $join_clause The join clause to add, e.g. JOIN table t ON t.id = s.id + * @return DatabaseQueryBuilder the builder + */ + public function joinClause($join_clause) + { + if (!in_array($join_clause, $this->join_clauses)) + array_push($this->join_clauses, $join_clause); + return $this; + } + + /** + * Pull back the join clauses ready to be added to a SQL statement + * + * This is a half-way house into adding join caluses to this builder + * + * @return string The join caluses as one long string + */ + public function getJoins() + { + return implode(" ", $this->join_clauses); + } + + private function bindArrayAsList($values): string + { + $bound_list_sql = ""; + foreach ($values as $fieldname => $fieldvalue) + { + $bound_list_sql .= ", {$fieldname}=:" . $this->addBoundVariable($fieldvalue); + } + return substr($bound_list_sql, 2); // remove first comma + } + + private function bindArrayAsInsertValues($values): array + { + $value_names = ""; + $value_binds = ""; + foreach ($values as $fieldname => $fieldvalue) + { + $value_names .= ", {$fieldname}"; + $value_binds .= ", :" . $this->addBoundVariable($fieldvalue); + } + $value_names = substr($value_names, 2); // remove first comma + $value_binds = substr($value_binds, 2); // remove first comma + + return array("names" => $value_names, "binds" => $value_binds); + } + + private function addBoundVariable($value): int + { + array_push($this->query_bound_values, $value); + return sizeof($this->query_bound_values); + } +} diff --git a/api/src/Database/Type/MySQL.php b/api/src/Database/Type/MySQL.php index 67bdee64a..19ca994f6 100644 --- a/api/src/Database/Type/MySQL.php +++ b/api/src/Database/Type/MySQL.php @@ -2,40 +2,234 @@ namespace SynchWeb\Database\Type; -use MySQLi; -use SynchWeb\Database\DatabaseParent; -use SynchWeb\Database\DatabaseInterface; use SqlFormatter; +use SynchWeb\Database\DatabaseParent; -class MySQL extends DatabaseParent implements DatabaseInterface -{ - +class MySQL extends DatabaseParent { + /** @var string */ protected $type = 'mysql'; - - var $debug = False; - var $stat = ''; - var $stats = False; - var $transaction = False; - var $errors = 0; - var $wsrep_sync = False; - - function __construct($user, $pass, $db, $port = null) + /** @var string */ + private $lastQuery = ''; // provide a way of retrieving the last query run - by storing the data - can then call getLastQuery() - primarily for testing + /** @var array */ + private $lastArgs = array(); + /** @var bool */ + private $transaction = False; + /** @var int */ + private $errors = 0; + + const TABLES = array( + 'AdminActivity', + 'Log4Stat', + 'PHPSession', + + 'Person', + 'Permission', + 'UserGroup_has_Permission', + 'UserGroup', + 'UserGroup_has_Person', + + 'Proposal', + 'ProposalHasPerson', + 'BLSession', + 'Session_has_Person', + 'SessionType', + + 'DataCollection', + 'EnergyScan', + 'XFEFluorescenceSpectrum', + 'RobotAction', + 'DataCollectionGroup', + 'DataCollectionComment', + 'ImageQualityIndicators', + 'GridInfo', + 'Detector', + + 'AutoProcIntegration', + 'AutoProcScaling_has_Int', + 'AutoProcScaling', + 'AutoProc', + 'AutoProcScalingStatistics', + 'AutoProcProgram', + 'AutoProcProgramAttachment', + 'AutoProcProgramMessage', + + 'Screening', + 'ScreeningOutput', + 'ScreeningStrategy', + 'ScreeningStrategyWedge', + 'ScreeningStrategySubWedge', + 'ScreeningOutputLattice', + + 'BLSample', + 'Position', + 'DiffractionPlan', + 'Protein', + 'Crystal', + 'Container', + 'Dewar', + 'Shipping', + 'ShippingHasSession', + 'DewarTransportHistory', + 'DewarRegistry', + 'DewarRegistry_has_Proposal', + 'DewarReport', + 'CourierTermsAccepted', + + 'BLSubSample', + 'PDB', + 'Protein_has_PDB', + + // Stat Views + 'v_logonByHour', + 'v_logonByMonthDay', + 'v_logonByWeek', + 'v_logonByWeekDay', + 'v_logonByHour2', + 'v_logonByMonthDay2', + 'v_logonByWeek2', + 'v_logonByWeekDay2', + + // Projects + 'Project', + 'Project_has_User', + 'Project_has_Person', + 'Project_has_DCGroup', + 'Project_has_EnergyScan', + 'Project_has_XFEFSpectrum', + 'Project_has_Protein', + 'Project_has_BLSample', + 'Project_has_Session', + + // Calendar + 'CalendarHash', + + // Faults + 'BF_fault', + 'BF_component', + 'BF_subcomponent', + 'BF_system', + 'BF_system_beamline', + 'BF_component_beamline', + 'BF_subcomponent_beamline', + + // Lab Contact + 'LabContact', + 'Laboratory', + + // PDBStats + 'PDBEntry', + 'PDBEntry_has_AutoProcProgram', + + // Protein -> Component + 'ConcentrationType', + 'ComponentType', + 'Component_has_SubType', + 'ComponentSubType', + 'BLSampleType_has_Component', + + // VMXi + 'ContainerInspection', + 'Imager', + 'Screen', + 'ScreenComponentGroup', + 'ScreenComponent', + 'Schedule', + 'ScheduleComponent', + 'InspectionType', + 'BLSampleImage', + 'BLSampleImageScore', + 'BLSampleImageAutoScoreSchema', + 'BLSampleImageAutoScoreClass', + 'BLSampleImage_has_AutoScoreClass', + + // Queuing + 'ContainerQueueSample', + 'ContainerQueue', + + // Container DB + 'ContainerHistory', + 'ContainerRegistry', + 'ContainerRegistry_has_Proposal', + 'ContainerReport', + + + 'ComponentLattice', + + // To be removed + 'Image', + + // Exp Planning + 'Detector', + 'DataCollectionPlan_has_Detector', + 'ScanParametersService', + 'ScanParametersModel', + 'BLSample_has_DataCollectionPlan', + + + 'DataCollectionFileAttachment', + + // Fl maps + 'XRFFluorescenceMapping', + 'XRFFluorescenceMappingROI', + + 'GridImageMap', + + // EM + 'MotionCorrection', + 'MotionCorrectionDrift', + 'CTF', + 'Movie', + + // Sample groups + 'BLSampleGroup', + 'BLSampleGroup_has_BLSample', + + // Processing + 'ProcessingJob', + 'ProcessingJobParameter', + 'ProcessingJobImageSweep', + 'ProcessingPipeline', + 'ProcessingPipelineCategory', + + 'BeamLineSetup', + + // Phasing + 'Phasing', + 'Phasing_has_Scaling', + 'PhasingProgramRun', + 'PhasingProgramAttachment', + + // Xray Centring + 'XrayCentring', + 'XrayCentringResult', + 'XrayCentring', + + 'BeamCalendar', + 'SpaceGroup', + + // MR + 'MXMRRun', + 'MXMRRunBlob', + ); + + function __construct($conn) { - list($host, $dbn) = explode('/', $db); - if (!$port) $port = ini_get("mysqli.default_port"); - $this->conn = new mysqli($host, $user, $pass, $dbn, $port); - mysqli_set_charset($this->conn, "utf8"); + parent::__construct($conn); + + mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // throw exceptions. - if (mysqli_connect_errno()) { + if (mysqli_connect_errno()) + { $this->error('There was an error connecting to MySQL: ', htmlentities(mysqli_connect_errno())); } } // wsrep_sync_wait waits for cluster replication on a mariadb cluster - function wait_rep_sync($state=false) + function wait_rep_sync($state = false) { $ver = $this->conn->server_info; - if (stripos($ver, 'maria')) { + if (stripos($ver, 'maria')) + { $this->pq("SET SESSION wsrep_sync_wait=:1", array($state ? 1 : 0)); } } @@ -48,22 +242,25 @@ function start_transaction() function end_transaction() { - if ($this->errors > 0) $this->conn->rollback(); - else $this->conn->commit(); + if ($this->errors > 0) + $this->conn->rollback(); + else + $this->conn->commit(); $this->conn->autocommit(True); $this->transaction = False; - if ($this->errors > 0) $this->error('There was an error with MySQL', $this->conn->error . __LINE__); + if ($this->errors > 0) + $this->error('There was an error with MySQL', $this->conn->error . __LINE__); $this->errors = 0; } - function paginate($query, $args = array()) { // MySQL is Limit Start Row, Number // Oracle subselect is Start Row, End Row - if (sizeof($args)) $args[sizeof($args) - 1] = $args[sizeof($args) - 1] - $args[sizeof($args) - 2]; + if (sizeof($args)) + $args[sizeof($args) - 1] = $args[sizeof($args) - 1] - $args[sizeof($args) - 2]; return $this->pq("$query LIMIT ?,?", $args); } @@ -73,19 +270,36 @@ function oracle2mysql($query, $args) // Allow for Oracle style :1, :2 out of order preg_match_all('/\:(\d+)/', $query, $mat); $rearranged_args = array(); - if ($this->debug) print_r(array('old', $args)); - foreach ($mat[1] as $id) { + if ($this->debug) + { + print_r('Old ordering:'); + var_dump($args); + } + + // TODO: would be nice to include this check - the danger is, is that there are existing queries where args are specified but not fully used - which would now break + // - on initial inspection, this appears to be the case - e.g. see the /current page (which suggests we should be fixing these things...) + // if (sizeof($args) != sizeof($mat[0])) { + // $this->error("Error preparing SQL query", "Incomplete parameter set supplied with SQL query: expected " . sizeof($mat) . " parameters, received " . sizeof($args)); + // } + + foreach ($mat[1] as $id) + { $aid = $id - 1; $val = $args[$aid]; array_push($rearranged_args, $val); unset($args[$aid]); } - foreach ($args as $remain) { + foreach ($args as $remain) + { array_push($rearranged_args, $remain); } - if ($this->debug) print_r(array('rearr', $rearranged_args)); + if ($this->debug) + { + print_r('Rearranged ordering:'); + var_dump($rearranged_args); + } $args = $rearranged_args; // Replace oracle :1 placeholder with ? @@ -133,238 +347,74 @@ function oracle2mysql($query, $args) return array($query, $args); } - function tablelookup($query) { - $tables = array('AdminActivity', - 'Log4Stat', - 'PHPSession', - - 'Person', - 'Permission', - 'UserGroup_has_Permission', - 'UserGroup', - 'UserGroup_has_Person', - - 'Proposal', - 'ProposalHasPerson', - 'BLSession', - 'Session_has_Person', - 'SessionType', - - 'DataCollection', - 'EnergyScan', - 'XFEFluorescenceSpectrum', - 'RobotAction', - 'DataCollectionGroup', - 'DataCollectionComment', - 'ImageQualityIndicators', - 'GridInfo', - 'Detector', - - 'AutoProcIntegration', - 'AutoProcScaling_has_Int', - 'AutoProcScaling', - 'AutoProc', - 'AutoProcScalingStatistics', - 'AutoProcProgram', - 'AutoProcProgramAttachment', - 'AutoProcProgramMessage', - - 'Screening', - 'ScreeningOutput', - 'ScreeningStrategy', - 'ScreeningStrategyWedge', - 'ScreeningStrategySubWedge', - 'ScreeningOutputLattice', - - 'BLSample', - 'Position', - 'DiffractionPlan', - 'Protein', - 'Crystal', - 'Container', - 'Dewar', - 'Shipping', - 'ShippingHasSession', - 'DewarTransportHistory', - 'DewarRegistry', - 'DewarRegistry_has_Proposal', - 'DewarReport', - 'CourierTermsAccepted', - - 'BLSubSample', - 'PDB', - 'Protein_has_PDB', - - // Stat Views - 'v_logonByHour', - 'v_logonByMonthDay', - 'v_logonByWeek', - 'v_logonByWeekDay', - 'v_logonByHour2', - 'v_logonByMonthDay2', - 'v_logonByWeek2', - 'v_logonByWeekDay2', - - // Projects - 'Project', - 'Project_has_User', - 'Project_has_Person', - 'Project_has_DCGroup', - 'Project_has_EnergyScan', - 'Project_has_XFEFSpectrum', - 'Project_has_Protein', - 'Project_has_BLSample', - 'Project_has_Session', - - // Calendar - 'CalendarHash', - - // Faults - 'BF_fault', - 'BF_component', - 'BF_subcomponent', - 'BF_system', - 'BF_system_beamline', - 'BF_component_beamline', - 'BF_subcomponent_beamline', - - // Lab Contact - 'LabContact', - 'Laboratory', - - // PDBStats - 'PDBEntry', - 'PDBEntry_has_AutoProcProgram', - - // Protein -> Component - 'ConcentrationType', - 'ComponentType', - 'Component_has_SubType', - 'ComponentSubType', - 'BLSampleType_has_Component', - - // VMXi - 'ContainerInspection', - 'Imager', - 'Screen', - 'ScreenComponentGroup', - 'ScreenComponent', - 'Schedule', - 'ScheduleComponent', - 'InspectionType', - 'BLSampleImage', - 'BLSampleImageScore', - 'BLSampleImageAutoScoreSchema', - 'BLSampleImageAutoScoreClass', - 'BLSampleImage_has_AutoScoreClass', - - // Queuing - 'ContainerQueueSample', - 'ContainerQueue', - - // Container DB - 'ContainerHistory', - 'ContainerRegistry', - 'ContainerRegistry_has_Proposal', - 'ContainerReport', - - - 'ComponentLattice', - - // To be removed - 'Image', - - // Exp Planning - 'Detector', - 'DataCollectionPlan_has_Detector', - 'ScanParametersService', - 'ScanParametersModel', - 'BLSample_has_DataCollectionPlan', - - - 'DataCollectionFileAttachment', - - // Fl maps - 'XRFFluorescenceMapping', - 'XRFFluorescenceMappingROI', - - 'GridImageMap', - - // EM - 'MotionCorrection', - 'MotionCorrectionDrift', - 'CTF', - 'Movie', - - // Sample groups - 'BLSampleGroup', - 'BLSampleGroup_has_BLSample', - - // Processing - 'ProcessingJob', - 'ProcessingJobParameter', - 'ProcessingJobImageSweep', - 'ProcessingPipeline', - 'ProcessingPipelineCategory', - - 'BeamLineSetup', - - // Phasing - 'Phasing', - 'Phasing_has_Scaling', - 'PhasingProgramRun', - 'PhasingProgramAttachment', - - // Xray Centring - 'XrayCentringResult', - - 'BeamCalendar', - 'SpaceGroup', - - // MR - 'MXMRRun', - 'MXMRRunBlob', - ); - - foreach ($tables as $table) { - $query = str_replace(" " . strtolower($table) . " ", " $table ", $query); - $query = str_replace(" " . strtolower($table) . "\n", " $table\n", $query); - $query = preg_replace("/ " . strtolower($table) . '$/', " $table", $query); + foreach (self::TABLES as $table) + { + $lowerCaseTable = strtolower($table); + $query = str_replace(" " . $lowerCaseTable . " ", " $table ", $query); + $query = str_replace(" " . $lowerCaseTable . "\n", " $table\n", $query); + $query = preg_replace("/ " . $lowerCaseTable . '$/', " $table", $query); } - // print_r(array($query)); - return $query; } + // return the last SQL query executed in fully expanded form (i.e. including binded params) - this is mainly for testing purposes (so ideally would not be part of the actual class...) + function getLastQuery() + { + echo '
'; + $sql = $this->lastQuery; + for ($i = 1; $i < count($this->lastArgs); $i++) + { + $val = $this->lastArgs[$i]; + if (is_string($val)) + { + $val = "'" . $val . "'"; + } + else if (is_null($val)) + { + $val = 'null'; + } + $sql = preg_replace('/\?/', $val, $sql, 1); + } + $sql = preg_replace('/\\n/', '', $sql); // replace newlines + return preg_replace('/\s\s+/', ' ', $sql); // remove excess spaces + } + function pq($query, $args = array(), $upperCaseKeys = true) { list($query, $args) = $this->oracle2mysql($query, $args); $query = $this->tablelookup($query); - if ($this->debug) { + if ($this->debug) + { print '

MySQL Debug

'; print SqlFormatter::format($query); - print_r($args); - // error_log($query); - // error_log(print_R($args)); + echo '
'; } $stmt = $this->conn->prepare($query); - if (!$stmt) { - if ($this->transaction) $this->errors++; - else $this->error('There was an error with MySQL', $this->conn->error . __LINE__); + if (!$stmt) + { + if ($this->transaction) + $this->errors++; + else + { + $err = mysqli_error($this->conn); + $this->error('There was an error with MySQL', $err); + return; + } } - if (sizeof($args)) { + if (sizeof($args)) + { $vtypes = array('NULL' => 'i', 'integer' => 'i', 'double' => 'd', 'string' => 's'); - $strfs = ''; - foreach ($args as $a) { + foreach ($args as $a) + { $t = gettype($a); $strfs .= $vtypes[$t]; } @@ -373,41 +423,63 @@ function pq($query, $args = array(), $upperCaseKeys = true) call_user_func_array(array(&$stmt, 'bind_param'), $this->refs($args)); } - if (!$stmt->execute()) { - if ($this->transaction) $this->errors++; - else $this->error('There was an error with MySQL', $this->conn->error . __LINE__); + $this->lastQuery = $query; + $this->lastArgs = $args; + if ($this->debug) + { + print_r("Full SQL query: " . $this->getLastQuery()); + } + + if (!$stmt->execute()) + { + if ($this->transaction) + $this->errors++; + else + { + $err = mysqli_error($this->conn); + $this->error('There was an error with MySQL', $err); + return; + } } $data = array(); - if (strpos($query, 'SELECT') !== false) { - if (PHP_VERSION_ID <= 50401) { - $r53 = new \SynchWeb\Database\Type\Result53(); - $data = $r53->invoke($stmt); - } else { - $result = $stmt->get_result(); - if ($result) { - if ($result->num_rows > 0) { - while ($row = $result->fetch_assoc()) { - $c = array(); - // oracle inheritance ;( - foreach ($row as $key => $val) { - if ($val !== null) { - if (gettype($val) == gettype(0.1)) $val = round($val, 5); - $val = strval($val); - } - if ($upperCaseKeys) { - $key = strtoupper($key); - } - $c[$key] = $val; + if (strpos($query, 'SELECT') !== false) + { + $result = $stmt->get_result(); + if ($result) + { + if ($result->num_rows > 0) + { + while ($row = $result->fetch_assoc()) + { + $c = array(); + // oracle inheritance ;( + foreach ($row as $key => $val) + { + if ($val !== null) + { + if (gettype($val) == gettype(0.1)) + $val = round($val, 5); + $val = strval($val); + } + if ($upperCaseKeys) + { + $key = strtoupper($key); } - array_push($data, $c); + $c[$key] = $val; } + array_push($data, $c); } } } } - if ($this->debug) print_r(array('rows', sizeof($data))); + if ($this->debug) + { + echo '
'; + print_r('row count: ' . sizeof($data)); + echo '
'; + } // Need mysqlnd for this :( // $result = $stmt->get_result(); @@ -425,82 +497,60 @@ function pq($query, $args = array(), $upperCaseKeys = true) $stmt->close(); return $data; - } // Union multiple queries that take the same arguments and return the same columns // Query can optionally be wrapped, where :QUERY will be replaced with the inner query: // $wrapper = 'SELECT * FROM (:QUERY) GROUP BY id'; - function union($queries, $args, $all=false, $wrapper=null) { + function union($queries, $args, $all = false, $wrapper = null) + { $nargs = sizeof($args); $all_args = array(); $union_kw = $all ? 'UNION ALL' : 'UNION'; - foreach ($queries as $i => &$query) { + foreach ($queries as $i => &$query) + { $offset = $i * $nargs; - $query = preg_replace_callback('/\:(\d+)/', - function($mat) use ($offset) { - return ':'.(intval($mat[1]) + $offset); - }, - $query); + $query = preg_replace_callback('/\:(\d+)/', + function ($mat) use ($offset) + { + return ':' . (intval($mat[1]) + $offset); + }, + $query); $all_args = array_merge($all_args, $args); - } + } $union = implode("\n$union_kw\n", $queries); - if ($wrapper) { + if ($wrapper) + { $union = preg_replace('/:QUERY/', $union, $wrapper); } return $this->pq($union, $all_args); } - function set_explain($exp) - { - - } - - function get_result($Statement) - { - $RESULT = array(); - $Statement->store_result(); - for ($i = 0; $i < $Statement->num_rows; $i++) { - $Metadata = $Statement->result_metadata(); - $PARAMS = array(); - while ($Field = $Metadata->fetch_field()) { - $PARAMS[] = &$RESULT[$i][$Field->name]; - } - call_user_func_array(array($Statement, 'bind_result'), $PARAMS); - $Statement->fetch(); - } - return $RESULT; - } - - function refs($arr) + private function refs($arr) { $refs = array(); - foreach ($arr as $key => $value) { - $refs[$key] = &$arr[$key]; + foreach ($arr as $key => $value) + { + $refs[$key] = & $arr[$key]; } return $refs; } - function read($field) { return $field; } - function id() { - return $this->conn->insert_id; + return mysqli_insert_id($this->conn); } - function close() { - // if (!$this->conn->connect_error) $this->conn->close(); - if ($this->conn) $this->conn->close(); - } - - + if ($this->conn) + $this->conn->close(); + } } diff --git a/api/src/Database/Type/PureMySQL.php b/api/src/Database/Type/PureMySQL.php new file mode 100644 index 000000000..8ab7d786b --- /dev/null +++ b/api/src/Database/Type/PureMySQL.php @@ -0,0 +1,262 @@ +error('There was an error connecting to MySQL: ', htmlentities(mysqli_connect_errno())); + } + } + + // wsrep_sync_wait waits for cluster replication on a mariadb cluster + function wait_rep_sync($state = false) + { + # Empty - DatabaseFactory checks this exists + } + + function start_transaction() + { + $this->transaction = True; + $this->conn->autocommit(False); + } + + function end_transaction() + { + if ($this->errors > 0) + $this->conn->rollback(); + else + $this->conn->commit(); + + $this->conn->autocommit(True); + $this->transaction = False; + + if ($this->errors > 0) + $this->error('There was an error with MySQL', $this->conn->error . __LINE__); + $this->errors = 0; + } + + function paginate($query, $args = array()) + { + // MySQL is Limit Start Row, Number + // Oracle subselect is Start Row, End Row + if (sizeof($args)) + $args[sizeof($args) - 1] = $args[sizeof($args) - 1] - $args[sizeof($args) - 2]; + return $this->pq("$query LIMIT ?,?", $args); + } + + + + // return the last SQL query executed in fully expanded form (i.e. including binded params) - this is mainly for testing purposes (so ideally would not be part of the actual class...) + function getLastQuery() + { + echo '
'; + $sql = $this->lastQuery; + for ($i = 1; $i < count($this->lastArgs); $i++) + { + $val = $this->lastArgs[$i]; + if (is_string($val)) + { + $val = "'" . $val . "'"; + } + else if (is_null($val)) + { + $val = 'null'; + } + $sql = preg_replace('/\?/', $val, $sql, 1); + } + $sql = preg_replace('/\\n/', '', $sql); // replace newlines + return preg_replace('/\s\s+/', ' ', $sql); // remove excess spaces + } + + function pq($query, $args = array(), $upperCaseKeys = true) + { + + if ($this->debug) + { + print '

MySQL Debug

'; + print SqlFormatter::format($query); + echo '
'; + } + + $stmt = $this->conn->prepare($query); + + if (!$stmt) + { + if ($this->transaction) + $this->errors++; + else + { + $err = mysqli_error($this->conn); + $this->error('There was an error with MySQL', $err); + return; + } + } + + if (sizeof($args)) + { + $vtypes = array('NULL' => 'i', 'integer' => 'i', 'double' => 'd', 'string' => 's'); + + $strfs = ''; + foreach ($args as $a) + { + $t = gettype($a); + $strfs .= $vtypes[$t]; + } + + array_unshift($args, $strfs); + call_user_func_array(array(&$stmt, 'bind_param'), $this->refs($args)); + } + + $this->lastQuery = $query; + $this->lastArgs = $args; + if ($this->debug) + { + print_r("Full SQL query: " . $this->getLastQuery()); + } + + if (!$stmt->execute()) + { + if ($this->transaction) + $this->errors++; + else + { + $err = mysqli_error($this->conn); + $this->error('There was an error with MySQL', $err); + return; + } + } + + $data = array(); + if (strpos($query, 'SELECT') !== false) + { + $result = $stmt->get_result(); + if ($result) + { + if ($result->num_rows > 0) + { + while ($row = $result->fetch_assoc()) + { + $c = array(); + // oracle inheritance ;( + foreach ($row as $key => $val) + { + if ($val !== null) + { + if (gettype($val) == gettype(0.1)) + $val = round($val, 5); + $val = strval($val); + } + if ($upperCaseKeys) + { + $key = strtoupper($key); + } + $c[$key] = $val; + } + array_push($data, $c); + } + } + } + } + + if ($this->debug) + { + echo '
'; + print_r('row count: ' . sizeof($data)); + echo '
'; + } + + // Need mysqlnd for this :( + // $result = $stmt->get_result(); + + // $data = array(); + + // if ($result) { + // if($result->num_rows > 0) { + // while($row = $result->fetch_assoc()) { + // array_push($data, array_change_key_case($row, CASE_UPPER)); + // } + // } + // } + + $stmt->close(); + + return $data; + } + + // Union multiple queries that take the same arguments and return the same columns + // Query can optionally be wrapped, where :QUERY will be replaced with the inner query: + // $wrapper = 'SELECT * FROM (:QUERY) GROUP BY id'; + function union($queries, $args, $all = false, $wrapper = null) + { + $nargs = sizeof($args); + $all_args = array(); + $union_kw = $all ? 'UNION ALL' : 'UNION'; + foreach ($queries as $i => &$query) + { + $offset = $i * $nargs; + $query = preg_replace_callback('/\:(\d+)/', + function ($mat) use ($offset) + { + return ':' . (intval($mat[1]) + $offset); + }, + $query); + $all_args = array_merge($all_args, $args); + } + + $union = implode("\n$union_kw\n", $queries); + if ($wrapper) + { + $union = preg_replace('/:QUERY/', $union, $wrapper); + } + + return $this->pq($union, $all_args); + } + + private function refs($arr) + { + $refs = array(); + foreach ($arr as $key => $value) + { + $refs[$key] = & $arr[$key]; + } + return $refs; + } + + function read($field) + { + return $field; + } + + function id() + { + return mysqli_insert_id($this->conn); + } + + function close() + { + if ($this->conn) + $this->conn->close(); + } +} \ No newline at end of file diff --git a/api/src/Database/Type/Result53.php b/api/src/Database/Type/Result53.php deleted file mode 100644 index 30e2f2d50..000000000 --- a/api/src/Database/Type/Result53.php +++ /dev/null @@ -1,31 +0,0 @@ -result_metadata(); - while ($field = $meta->fetch_field()) { - array_push($params, &$row[$field->name]); - } - call_user_func_array(array($stmt, 'bind_result'), $params); - $data = array(); - while ($stmt->fetch()) { - $c = array(); - // Oracle returns all values as strings - Need to be consistent :( - foreach ($row as $key => $val) { - if ($val !== null) { - if (gettype($val) == gettype(0.1)) $val = round($val, 5); - $val = strval($val); - } - $c[strtoupper($key)] = $val; - } - $data[] = $c; - } - return $data; - } - -} diff --git a/api/src/Dispatch.php b/api/src/Dispatch.php index 28a45e0c1..ff311a6f9 100644 --- a/api/src/Dispatch.php +++ b/api/src/Dispatch.php @@ -15,35 +15,53 @@ function __construct(Slim $app, $db, $user) $this->user = $user; } - // Generate routes for Slim - function dispatch() + private function setupControllerClasses($filesWildCard, $app, $db, $user, $namespaceName) { - $app = $this->app; - $db = $this->db; - $user = $this->user; // Get names of all files in pages directory - foreach (glob(__DIR__ . '/Page/*.php') as $file_path) { + foreach (glob(__DIR__ . $filesWildCard) as $file_path) + { // Determine class name from file path $class_name = basename($file_path, '.php'); // Determine routes for each class, where base URL is / - $app->group('/' . strtolower($class_name), function () use ($app, $db, $user, $class_name) { - $full_class_name = 'SynchWeb\\Page\\' . $class_name; - - // Instantiate each class if class has been defined - // This merges the routes from all classes - if (class_exists($full_class_name)) { + $app->group('/' . strtolower($class_name), function () use ($app, $db, $user, $class_name, $namespaceName) + { + $full_class_name = $namespaceName . $class_name; + if (class_exists($full_class_name)) + { new $full_class_name($app, $db, $user); } }); } - $this->app->notFound(function () use ($app) { + } + + // Generate routes for Slim + function dispatch() + { + $app = $this->app; + $db = $this->db; + $user = $this->user; + $this->setupControllerClasses('/Page/*.php', $app, $db, $user, 'SynchWeb\\Page\\'); + + // add specific routes which break the old convention + $app->group('/users', function () use ($app) + { + $app->container['userController']; + }); + + $app->group('/assign', function () use ($app) + { + $app->container['assignController']; + }); + + $this->app->notFound(function () use ($app) + { $app->halt(404, json_encode(array('status' => 404, 'message' => 'not found'))); }); $app->run(); } -} +} \ No newline at end of file diff --git a/api/src/Downstream/BigEPPhasing.php b/api/src/Downstream/BigEPPhasing.php index 8a2068d8a..63458784f 100644 --- a/api/src/Downstream/BigEPPhasing.php +++ b/api/src/Downstream/BigEPPhasing.php @@ -25,13 +25,17 @@ function results() { } $image = $this->_get_image(); - $dat['IMAGE'] = file_exists($image); + $dat['IMAGE'] = file_exists($image) || file_exists($image.'.gz'); $model = $this->_get_attachments('big_ep_model_ispyb.json'); - $dat['HASMODEL'] = $model && file_exists($model['FILE']); + $dat['HASMODEL'] = $model && (file_exists($model['FILE']) || file_exists($model['FILE'].'.gz')); if ($model) { if (file_exists($model['FILE'])) { $json_str = file_get_contents($model['FILE']); + } elseif (file_exists($model['FILE'].'.gz')) { + $json_str = gzdecode(file_get_contents($model['FILE'].'.gz')); + } + if (isset($json_str)) { $json_data = json_decode($json_str, true); foreach ( array( diff --git a/api/src/Downstream/Type/Dimple.php b/api/src/Downstream/Type/Dimple.php index a180d85a5..932487436 100644 --- a/api/src/Downstream/Type/Dimple.php +++ b/api/src/Downstream/Type/Dimple.php @@ -16,7 +16,8 @@ function _get_blobs() { return $this->db->pq( "SELECT view1, view2, view3, filepath FROM mxmrrunblob - WHERE mxmrrunid = :1", + WHERE mxmrrunid = :1 + AND filepath is not NULL", array($this->_mrrun["MXMRRUNID"]) ); } diff --git a/api/src/Downstream/Type/FastEp.php b/api/src/Downstream/Type/FastEp.php index 906a1e0e5..f6a3d54cc 100644 --- a/api/src/Downstream/Type/FastEp.php +++ b/api/src/Downstream/Type/FastEp.php @@ -16,83 +16,95 @@ function results() { $pdb = $this->_get_attachments("sad_fa.pdb"); if ($pdb) { if (file_exists($pdb["FILE"])) { - $pdb = file_get_contents($pdb["FILE"]); - foreach (explode("\n", $pdb) as $l) { - if (strpos($l, 'HETATM') !== false) { - $parts = preg_split('/\s+/', $l); - array_push($ats, array( - $parts[1], - $parts[5], - $parts[6], - $parts[7], - $parts[8], - )); - } - } + $pdb_cont = file_get_contents($pdb["FILE"]); + } elseif (file_exists($pdb['FILE'].'.gz')) { + $pdb_cont = gzdecode(file_get_contents($pdb['FILE'].'.gz')); + } + } - $dat['ATOMS'] = array_slice($ats, 0, 5); + if (isset($pdb_cont)) { + foreach (explode("\n", $pdb_cont) as $l) { + if (strpos($l, 'HETATM') !== false) { + $parts = preg_split('/\s+/', $l); + array_push($ats, array( + $parts[1], + $parts[5], + $parts[6], + $parts[7], + $parts[8], + )); + } } + + $dat['ATOMS'] = array_slice($ats, 0, 5); + } $lst = $this->_get_attachments("sad.lst"); if ($lst) { if (file_exists($lst['FILE'])) { - $p1 = array(); - $p2 = array(); - - $lst = file_get_contents($lst['FILE']); - $graph_vals = 0; - $gvals = array(); - foreach (explode("\n", $lst) as $l) { - if ( - strpos( - $l, - 'Estimated mean FOM and mapCC as a function of resolution' - ) !== false - ) { - $graph_vals = 1; - } - - if ($graph_vals && $graph_vals < 5) { - array_push($gvals, $l); - $graph_vals++; - } - - if ( - preg_match( - '/ Estimated mean FOM = (\d+.\d+)\s+Pseudo-free CC = (\d+.\d+)/', - $l, - $mat - ) - ) { - $dat['FOM'] = floatval($mat[1]); - $dat['CC'] = floatval($mat[2]); - } + $lst_cont = file_get_contents($lst['FILE']); + } elseif (file_exists($lst['FILE'].'.gz')) { + $lst_cont = gzdecode(file_get_contents($lst['FILE'].'.gz')); + } + } + + if (isset($lst_cont)) { + $p1 = array(); + $p2 = array(); + + $graph_vals = 0; + $gvals = array(); + foreach (explode("\n", $lst_cont) as $l) { + if ( + strpos( + $l, + 'Estimated mean FOM and mapCC as a function of resolution' + ) !== false + ) { + $graph_vals = 1; } - if (sizeof($gvals) > 0) { - $x = array_map( - 'floatval', - array_slice(explode(' - ', $gvals[1]), 1) - ); - $y = array_map( - 'floatval', - array_slice(preg_split('/\s+/', $gvals[2]), 2) - ); - $y2 = array_map( - 'floatval', - array_slice(preg_split('/\s+/', $gvals[3]), 2) - ); - - foreach ($x as $i => $v) { - array_push($p1, array(1.0 / pow($v, 2), $y[$i])); - array_push($p2, array(1.0 / pow($v, 2), $y2[$i])); - } + if ($graph_vals && $graph_vals < 5) { + array_push($gvals, $l); + $graph_vals++; } - $dat['PLOTS']['FOM'] = $p1; - $dat['PLOTS']['CC'] = $p2; + if ( + preg_match( + '/ Estimated mean FOM = (\d+.\d+)\s+Pseudo-free CC = (\d+.\d+)/', + $l, + $mat + ) + ) { + $dat['FOM'] = floatval($mat[1]); + $dat['CC'] = floatval($mat[2]); + } } + + if (sizeof($gvals) > 0) { + $x = array_map( + 'floatval', + array_slice(explode(' - ', $gvals[1]), 1) + ); + $y = array_map( + 'floatval', + array_slice(preg_split('/\s+/', $gvals[2]), 2) + ); + $y2 = array_map( + 'floatval', + array_slice(preg_split('/\s+/', $gvals[3]), 2) + ); + + foreach ($x as $i => $v) { + array_push($p1, array(1.0 / pow($v, 2), $y[$i])); + array_push($p2, array(1.0 / pow($v, 2), $y2[$i])); + } + } + + $dat['PLOTS']['FOM'] = $p1; + $dat['PLOTS']['CC'] = $p2; + } $results = new DownstreamResult($this); diff --git a/api/src/ImagingShared.php b/api/src/ImagingShared.php index 4722b47b5..074da8325 100644 --- a/api/src/ImagingShared.php +++ b/api/src/ImagingShared.php @@ -5,11 +5,10 @@ class ImagingShared { + private $db; + function __construct($db=null) { global $isp; - if (!$db) { - $db = Database::get(); - } $this->db = $db; } diff --git a/api/src/Model/Services/AssignData.php b/api/src/Model/Services/AssignData.php new file mode 100644 index 000000000..ed3e4bbaf --- /dev/null +++ b/api/src/Model/Services/AssignData.php @@ -0,0 +1,120 @@ +db = $db; + } + + # ------------------------------------------------------------------------ + # Get a container + # $visitId is an aggregate of the group name, proposal number and visit number - e.g. group: 'mx', proposal: 28866, visit: 4 -> $visitId = 'mx28866-4' + function getContainer($visitId, $containerId) + { + return $this->db->pq("SELECT d.dewarid,bl.beamlinename,c.containerid,c.code FROM container c + INNER JOIN dewar d ON d.dewarid = c.dewarid + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN blsession bl ON bl.proposalid = s.proposalid + INNER JOIN proposal p ON s.proposalid = p.proposalid + WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) LIKE :1 + AND c.containerid=:2", array($visitId, $containerId)); + } + + function assignContainer($container, $location) + { + $this->updateDewar($container['DEWARID'], 'processing'); + + $this->updateContainerAndHistory($container['CONTAINERID'], 'processing', $container['BEAMLINENAME'], $location); + + $this->updateDewarHistory($container['DEWARID'], 'processing', $container['BEAMLINENAME'], $container['CODE'] . ' => ' . $location); + } + + function unassignContainer($container) + { + $this->updateContainerAndHistory($container['CONTAINERID'], 'at facility', '', ''); + } + + function updateContainerAndHistory($containerId, $status, $beamlineName, $location) + { + $this->updateContainer($containerId, $status, $beamlineName, $location); + $this->updateContainerHistory($containerId, $status, $beamlineName, $location); + } + + function updateContainerHistory($containerId, $status, $beamlineName, $location) + { + $this->db->pq("INSERT INTO containerhistory + (containerid, status, location, beamlinename) + VALUES (:1,:2,:3,:4)", array($containerId, $status, $location, $beamlineName)); + } + + function updateContainer($containerId, $status, $beamlineName, $location) + { + $this->db->pq("UPDATE container + SET beamlinelocation=:1, samplechangerlocation=:2, containerstatus=:3 + WHERE containerid=:4", array($beamlineName, $location, $status, $containerId)); + } + + function getDewar($dewarId, $proposalId, $visitId) + { + $where = "p.proposalid=:1"; + $arg = $proposalId; + if ($visitId) + { + $where = "CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) LIKE :1"; + $arg = $visitId; + } + + return $this->db->pq("SELECT d.dewarid FROM dewar d + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN blsession bl ON bl.proposalid = s.proposalid + INNER JOIN proposal p ON s.proposalid = p.proposalid + WHERE $where AND d.dewarid=:2", array($arg, $dewarId)); + } + + function updateDewar($dewarId, $status) + { + if ($status == 'unprocessing') + $status = 'at facility'; + $this->db->pq("UPDATE dewar SET dewarstatus=:1 WHERE dewarid=:2", array($status, $dewarId)); + } + + function deactivateDewar($dewarId) + { + $location = $this->db->pq("SELECT storagelocation FROM dewar WHERE dewarid=:1", array($dewarId)); + $this->updateDewarHistory($dewarId, 'unprocessing', $location[0]['STORAGELOCATION']); + + $conts = $this->db->pq("SELECT containerid FROM container WHERE dewarid=:1", array($dewarId)); + foreach ($conts as $container) + { + $this->updateContainerAndHistory($container['CONTAINERID'], 'at facility', '', ''); + } + } + + function updateDewarHistory($did, $status, $beamline = null, $additionalStatusDetail = null) + { + $st = $status; + if ($additionalStatusDetail) + $st .= ' (' . $additionalStatusDetail . ')'; + $loc = Utils::getValueOrDefault($beamline, ''); + $this->db->pq("INSERT INTO dewartransporthistory + (dewarid, dewarstatus, storagelocation, arrivaldate) + VALUES (:1, :2, :3, CURRENT_TIMESTAMP)", array($did, $st, $loc)); + + $this->updateDewar($did, $status); + } + + function getContainerBarcodesForProposal($proposalId) + { + return $this->db->pq("SELECT cr.barcode + FROM containerregistry cr + INNER JOIN containerregistry_has_proposal crhp ON crhp.containerregistryid = cr.containerregistryid + WHERE crhp.proposalid = :1", array($proposalId)); + } +} diff --git a/api/src/Model/Services/AuthenticationData.php b/api/src/Model/Services/AuthenticationData.php new file mode 100644 index 000000000..a7d96457a --- /dev/null +++ b/api/src/Model/Services/AuthenticationData.php @@ -0,0 +1,103 @@ +db = $db; + } + + function isUserLoggedIn($userId): bool + { + $userc = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($userId)); + + return sizeof($userc) > 0; + } + + + function getOneTimeUseToken($tokenId) + { + return $this->db->pq("SELECT o.validity, pe.personid, pe.login, CONCAT(p.proposalcode, p.proposalnumber) as prop + FROM SW_onceToken o + INNER JOIN proposal p ON p.proposalid = o.proposalid + INNER JOIN person pe ON pe.personid = o.personid + WHERE token=:1", array($tokenId)); + } + + function deleteOneTimeUseToken($tokenId) + { + $this->db->pq("DELETE FROM SW_onceToken WHERE token=:1", array($tokenId)); + } + + function deleteOldOneTimeUseTokens() + { + # Remove tokens more than 10 seconds old, they should have been used + $this->db->pq("DELETE FROM SW_onceToken WHERE recordTimeStamp < NOW() - INTERVAL 10 SECOND"); + } + + + function getUser($loginId): User + { + $user = null; + $result = $this->db->pq("SELECT cache, personid, givenname, familyname FROM person p WHERE login=:1", array($loginId)); + + if (sizeof($result)) + { + $personId = intval($result[0]['PERSONID']); + + $perms = array(); + $groups = array(); + $permsAndGroups = $this->db->pq("SELECT p.type, g.name as usergroup + FROM permission p + INNER JOIN usergroup_has_permission uhp ON uhp.permissionid = p.permissionid + INNER JOIN usergroup g ON g.usergroupid = uhp.usergroupid + INNER JOIN usergroup_has_person uhpe ON uhpe.usergroupid = g.usergroupid + WHERE uhpe.personid=:1", array($personId)); + + foreach ($permsAndGroups as $p) + { + if (!in_array($p['TYPE'], $perms)) + { + array_push($perms, $p['TYPE']); + } + if (!in_array($p['USERGROUP'], $groups)) + { + array_push($groups, $p['USERGROUP']); + } + } + + $cache = array(); + if ($result[0]['CACHE']) + { + array_push($cache, json_decode($this->db->read($result[0]['CACHE']), True)); + } + + $user = new User($loginId, $personId, $result[0]['GIVENNAME'], $result[0]['FAMILYNAME'], $perms, $groups, $cache); + } + return $user; + } + + function updateActivityTimestamp($loginId) + { + if (Utils::ShouldLogUserActivityToDB($loginId)) + { + $chk = $this->db->pq("SELECT TIMESTAMPDIFF('SECOND', datetime, CURRENT_TIMESTAMP) AS lastupdate, comments FROM adminactivity WHERE username LIKE :1", array($loginId)); + if (sizeof($chk)) + { + if ($chk[0]['LASTUPDATE'] > 20) + $this->db->pq("UPDATE adminactivity SET datetime=CURRENT_TIMESTAMP WHERE username=:1", array($loginId)); + } + } + } +} \ No newline at end of file diff --git a/api/src/Model/Services/UserData.php b/api/src/Model/Services/UserData.php new file mode 100644 index 000000000..2a01cdbb5 --- /dev/null +++ b/api/src/Model/Services/UserData.php @@ -0,0 +1,426 @@ +db = $db; + } + + function getGroups($gid = null) + { + $where = ''; + $args = array(); + + if ($gid) + { + $where = 'WHERE g.usergroupid=:1'; + array_push($args, $gid); + } + + return $this->db->pq("SELECT g.usergroupid, g.name, count(uhp.personid) as users + FROM usergroup g + LEFT OUTER JOIN usergroup_has_person uhp ON uhp.usergroupid = g.usergroupid + $where + GROUP BY g.usergroupid, g.name + ORDER BY g.name", $args); + } + + + function addGroup($groupName) + { + $this->db->pq('INSERT INTO usergroup (name) VALUES (:1) RETURNING usergroupid INTO :id', array($groupName)); + return $this->db->id(); + } + + + function updateGroup($gid, $groupName) + { + $this->db->pq('UPDATE usergroup SET name=:1 WHERE usergroupid=:2', array($groupName, $gid)); + } + + function addGroupPermission($gid, $pid) + { + $this->db->pq("INSERT INTO usergroup_has_permission (usergroupid, permissionid) VALUES (:1,:2)", array($gid, $pid)); + return $this->db->id(); + } + + + function removeGroupPermission($userGroupId, $permisionId) + { + $this->db->pq("DELETE FROM usergroup_has_permission WHERE usergroupid=:1 and permissionid=:2", array($userGroupId, $permisionId)); + } + + private function addPersonOrProposalSearch($proposalid, $personId, &$args): string + { + $whereClause = ' AND (prhp.proposalid=:' . (sizeof($args) + 1) . ' OR lc.proposalid=:' . (sizeof($args) + 2) . ' OR p.personid=:' . (sizeof($args) + 3) . ')'; + array_push($args, $proposalid); + array_push($args, $proposalid); + array_push($args, $personId); + return $whereClause; + } + + function getUsers($getCount, $isStaffMember, $stringMatch, $page, $sortBy = null, $pid=null, $proposalid = null, $personId = null, $isManager = false, $currentUserId = null, $gid = null, $sid = null, $pjid = null, $visitName = null, $perPage = 15, $isAscending = true, $isAll = false, $onlyLogins = false) + { + $args = array(); + + // set up start and end + $start = 0; + $pp = $perPage; + $end = $pp; + + if ($page) + { + $pg = $page - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + // Set initial where clause restrict it to just users within logins unless it is for a all or a person id and only logins not set + if (!$onlyLogins and ($personId || $isAll)) + $where = '1=1'; + else + $where = 'p.login IS NOT NULL'; + + if ($personId == "" && $stringMatch == "" && $gid == "" && $sid == "" && $visitName == "" && $pjid == "") + { + //secured by making sure the user has access toproposalid + return $this->getUsersForProposal($where, $getCount, $page, $sortBy, $proposalid, $currentUserId, $perPage, $isAscending, $start, $end); + } + + $join = ''; + $extc = ''; + $group = 'GROUP BY p.personid'; + + // This blocks means that non-staff can only see users on their proposal, except when they looking at a visit + // (i.e. the proposal that was checkin page.php is added to the where clause) + if ((($personId && !$isManager) // if you not a manager: you looking for a person + || (!$isStaffMember && !$visitName)) // if you are not a staff member and not loking at a specific visit + || $pid) // if you are looking for user based on a proposal, but this + { + $where .= $this->addPersonOrProposalSearch($proposalid, $currentUserId, $args); + } + + if ($personId) + { + $where .= ' AND p.personid=:' . (sizeof($args) + 1); + array_push($args, $personId); + } + + if ($stringMatch) + { + $st = sizeof($args) + 1; + $where .= " AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%',:" . $st . + "),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%',:" . + ($st + 1) . "),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "),'%')))"; + for ($i = 0; $i < 3; $i++) + { + array_push($args, $stringMatch); + } + } + + // TODO: the following statements were not previously coded as mutually exclusive, however logically they must be as no attempt was made to extend the JOIN statement + // - it may be worth reviewing this, however. + if ($gid) + { + $join = 'INNER JOIN usergroup_has_person uhp ON uhp.personid = p.personid'; + $where .= ' AND uhp.usergroupid=:' . (sizeof($args) + 1); + array_push($args, $gid); + } + else if ($sid) + { + // TODO: this is an invalid DB table - does this need to be removed entirely? + $join = 'INNER JOIN blsession_has_person shp ON shp.personid = p.personid'; + $where .= ' AND shp.sessionid=:' . (sizeof($args) + 1); + array_push($args, $sid); + } + else if ($visitName) + { + $extc = "count(ses.sessionid) as visits, TO_CHAR(max(ses.startdate), 'DD-MM-YYYY') as last, shp.remote, shp.role,"; + $join = 'INNER JOIN session_has_person shp ON shp.personid = p.personid + INNER JOIN blsession s ON shp.sessionid = s.sessionid + INNER JOIN proposal pr ON pr.proposalid = s.proposalid + LEFT OUTER JOIN session_has_person shp2 ON p.personid = shp2.personid + LEFT OUTER JOIN blsession ses ON ses.sessionid = shp2.sessionid AND ses.startdate < s.startdate'; + $where .= " AND shp.remote IS NOT NULL AND CONCAT(pr.proposalcode, pr.proposalnumber, '-', s.visit_number) LIKE :" . (sizeof($args) + 1); + $group = 'GROUP BY p.personid, p.givenname, p.familyname, p.login'; + array_push($args, $visitName); + } + else if ($pjid) + { + $join = 'INNER JOIN project_has_person php ON p.personid = php.personid'; + $where .= ' AND php.projectid=:' . (sizeof($args) + 1); + $extc = "CONCAT(p.personid, '-', php.projectid) as ppid,"; + array_push($args, $pjid); + } + + if ($getCount) + { + $tot = $this->db->pq("SELECT count(distinct p.personid) as tot + FROM person p + LEFT OUTER JOIN proposalhasperson prhp ON prhp.personid = p.personid + LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid + $join + WHERE $where", $args); + + return sizeof($tot) ? intval($tot[0]['TOT']) : 0; + } + + array_push($args, $start); + array_push($args, $end); + + $order = 'p.familyname,p.givenname'; + if ($sortBy) + { + $cols = array('LOGIN' => 'p.login', 'GIVENNAME' => 'p.givenname', 'FAMILYNAME' => 'p.familyname'); + if (array_key_exists($sortBy, $cols)) + { + $dir = $isAscending ? 'ASC' : 'DESC'; + $order = $cols[$sortBy] . ' ' . $dir; + } + } + + $rows = $this->db->paginate("SELECT $extc p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country + FROM person p + LEFT OUTER JOIN proposalhasperson prhp ON prhp.personid = p.personid + LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid + LEFT OUTER JOIN laboratory l ON l.laboratoryid = p.laboratoryid + $join + WHERE $where + $group + ORDER BY $order", $args); + + foreach ($rows as &$r) + { + if ($r['PERSONID'] == $personId) + $r['FULLNAME'] .= ' [You]'; + } + + return $rows; + } + + function getUsersForProposal($where, $getCount, $page, $sortBy, $proposalid, $currentUserId, $perPage, $isAscending, $start, $end) + { + $args = array(); + + $where1 = $where . ' AND prhp.proposalid=:1'; + $where2 = $where . ' AND (lc.proposalid=:2 OR p.personid=:3)'; + + array_push($args, $proposalid); + array_push($args, $proposalid); + array_push($args, $currentUserId); + + if ($getCount) + { + $tot = $this->db->pq("select count(personId) as tot FROM + (SELECT p.personid + FROM person p + LEFT OUTER JOIN proposalhasperson prhp ON prhp.personid = p.personid + WHERE $where1 + UNION + SELECT p.personid + FROM person p + LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid + WHERE $where2) + AS PERSONS", $args); + + return sizeof($tot) ? intval($tot[0]['TOT']) : 0; + } + + array_push($args, $start); + array_push($args, $end); + + $order = 'p.familyname,p.givenname'; + if ($sortBy) + { + $cols = array('LOGIN' => 'p.login', 'GIVENNAME' => 'p.givenname', 'FAMILYNAME' => 'p.familyname'); + if (array_key_exists($sortBy, $cols)) + { + $dir = $isAscending ? 'ASC' : 'DESC'; + $order = $cols[$sortBy] . ' ' . $dir; + } + } + + $extc = "p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country"; + $rows = $this->db->paginate("(SELECT $extc + FROM person p + LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid + LEFT OUTER JOIN laboratory l ON l.laboratoryid = p.laboratoryid + WHERE $where1 + GROUP BY p.personid + ORDER BY $order) + UNION + (SELECT $extc + FROM person p + LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid + LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid + WHERE $where2 + GROUP BY p.personid + ORDER BY $order)", $args); + + return $rows; + } + + function checkLogin($loginId) + { + return $this->db->pq("SELECT login FROM person WHERE login=:1", array($loginId)); + } + + + function addUser($loginId, $givenName, $familyName, $emailAddress = null) + { + $this->db->pq( + "INSERT INTO person (login, givenname, familyname, emailaddress) VALUES (:1, :2, :3, :4)", + array($loginId, $givenName, $familyName, $emailAddress) + ); + return $this->db->id(); + } + + function getUser($userId, $proposalId, $personId) + { + return $this->db->pq("SELECT p.personid, p.laboratoryid + FROM person p + LEFT OUTER JOIN proposalhasperson php ON php.personid = p.personid + LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid + WHERE (p.personid=:1 OR php.proposalid=:2 OR lc.proposalid=:3) AND p.personid=:4", array($userId, $proposalId, $proposalId, $personId)); + } + + function updateUser($personId, $familyName, $givenName, $phoneNumber, $email) + { + (new DatabaseQueryBuilder($this->db)) + ->patch("FAMILYNAME", $familyName) + ->patch("GIVENNAME", $givenName) + ->patch("PHONENUMBER", $phoneNumber) + ->patch("EMAILADDRESS", $email) + ->whereIdEquals("personid", $personId) + ->update("person"); + } + + function getLaboratory($laboratoryId) + { + return $this->db->pq("SELECT l.name, l.address, l.city, l.postcode, l.country + FROM laboratory l + WHERE l.laboratoryid=:1", array($laboratoryId)); + } + + function updateLaboratory($personId, $labName, $labAddress, $city, $postcode, $country, $laboratoryId = null) + { + $db_values_to_use = (new DatabaseQueryBuilder($this->db)) + ->patch("name", $labName) + ->patch("address", $labAddress) + ->patch("city", $city) + ->patch("postcode", $postcode) + ->patch("country", $country); + + if ($laboratoryId) + { + $db_values_to_use + ->whereIdEquals("laboratoryid", $laboratoryId) + ->update("laboratory"); + } + else + { + # TODO: the logic here appears dubious - may result in duplicate entries for labs, rather than reusing these? Perhaps this is ok, though... + $laboratoryId = $db_values_to_use->insert("laboratory"); + + $db_values_to_use = (new DatabaseQueryBuilder($this->db)) + ->patch("laboratoryid", $laboratoryId) + ->whereIdEquals("personid", $personId) + ->update("person"); + } + } + + function addGroupUser($personId, $gid) + { + $this->db->pq("INSERT INTO usergroup_has_person (usergroupid, personid) VALUES (:1,:2)", array($gid, $personId)); + return $this->db->id(); + } + + + function removeGroupUser($personId, $gid) + { + $this->db->pq("DELETE FROM usergroup_has_person WHERE usergroupid=:1 and personid=:2", array($gid, $personId)); + } + + function getPermissions($getCount = false, $s = null, $gid = null, $pid = null, $perPage = 15, $startPage = 0) + { + $args = array(); + $where = ''; + $join = ''; + + if ($gid) + { + $join = 'INNER JOIN usergroup_has_permission uhp ON uhp.permissionid = p.permissionid'; + $where = ' AND uhp.usergroupid=:' . (sizeof($args) + 1); + array_push($args, $gid); + } + + if ($pid) + { + $where .= ' AND p.permissionid=:' . (sizeof($args) + 1); + array_push($args, $pid); + } + + if ($s) + { + $st = sizeof($args) + 1; + $where .= " AND (lower(p.type) LIKE lower(CONCAT(CONCAT('%',:" . $st . "),'%')))"; + array_push($args, $s); + } + + if ($getCount) + { + $tot = $this->db->pq("SELECT count(p.permissionid) as tot + FROM permission p + $join + WHERE 1=1 $where", $args); + if (!sizeof($tot)) + { + return 0; + } + return intval($tot[0]['TOT']); + } + + $start = 0; + $pp = $perPage; + $end = $pp; + + if ($startPage) + { + $pg = $startPage - 1; + $start = $pg * $pp; + $end = $start + $pp; + } + array_push($args, $start); + array_push($args, $end); + + return $this->db->paginate("SELECT p.permissionid, p.type, p.description + FROM permission p + $join + WHERE 1=1 $where + ORDER BY p.type", $args); + } + + function addPermission($type, $description) + { + $this->db->pq('INSERT INTO permission (type,description) VALUES (:1,:2) RETURNING permissionid INTO :id', array($type, $description)); + return $this->db->id(); + } + + function updatePermission($pid, $type, $description = '') + { + $this->db->pq('UPDATE permission SET type=:1, description=:2 WHERE permissionid=:3', array($type, $description, $pid)); + } +} diff --git a/api/src/Model/User.php b/api/src/Model/User.php new file mode 100644 index 000000000..4a6341d72 --- /dev/null +++ b/api/src/Model/User.php @@ -0,0 +1,89 @@ +loginId = $loginId; + $this->personId = $personId; + $this->givenName = $givenName; + $this->familyName = $familyName; + $this->perms = $permissions; + $this->groups = $groups; + $this->cache = $cache; + } + + function hasPermission($permission) + { + return in_array($permission, $this->perms); + } + + function can($permission, $app) + { + if ($this->hasPermission($permission)) { + return true; + } + $app->halt(403, json_encode(array('status' => 403, 'message' => 'Access Denied', 'title' => 'You do not have the permission: ' . $permission))); + } + + + function isInGroup($group) + { + return in_array($group, $this->groups); + } + + // User cache - for saving partially filled forms, etc + function getFromCache($key) + { + return array_key_exists($key, $this->cache) ? $this->cache[$key] : null; + } + + function setInCache($key, $data) + { + if (in_array($key, $this->allowed_caches)) { + $this->cache[$key] = $data; + + return true; + } + return false; + } + + function getAdminType() + { + if (!$this->adminType) { + foreach ($this->perms as $p) { + if (strpos($p, '_admin')) { + $parts = explode('_', $p); + $this->adminType = $parts[0]; + break; + } + } + } + return $this->adminType; + } + + function __toString() + { + return $this->loginId; + } + + # For JSONing a user + function __toArray() + { + return array('login' => $this->loginId, 'personid' => $this->personId, 'permissions' => $this->perms); + } +} \ No newline at end of file diff --git a/api/src/Options.php b/api/src/Options.php index 895787617..750c3c460 100644 --- a/api/src/Options.php +++ b/api/src/Options.php @@ -21,7 +21,7 @@ private function _retrieve() { $this->options = array(); foreach ($options as $o) { - $this->options[$o['NAME']] = unserialize($o['VALUE']); + $this->options[$o['NAME']] = $o['VALUE']; if ($o['UI'] == 1) $this->public[$o['NAME']] = $this->options[$o['NAME']]; } diff --git a/api/src/Page.php b/api/src/Page.php index 32b55c8fc..72ffa2f98 100644 --- a/api/src/Page.php +++ b/api/src/Page.php @@ -7,939 +7,1141 @@ use HTMLPurifier_Config; use ReflectionClass; use Slim\Slim; -use xmlrpc_client; -use xmlrpcmsg; -use xmlrpcval; +use SynchWeb\Database\DatabaseParent; use SynchWeb\Queue; +use SynchWeb\Utils; class Page { - protected $app, $db, $user; - - var $require_staff = False; - var $staff = False; - var $visits = array(); - var $debug = False; - var $explain = False; - var $stats = False; - var $profile = False; - var $profiles = array(); - var $base; - - public static $dispatch = array(); - public static $arg_list = array(); - - private $generic_args = array('page' => '\d+', - 'per_page' => '\d+', - 'sort_by' => '\w+', - 'order' => 'desc|asc', - 'ty' => '\w+', - 's' => '[\w\s-]+', - 'prop' => '\w+\d+', - ); - - - var $sessionid; - var $proposalid; - - function _base() { - $rc = new ReflectionClass(get_class($this)); - return dirname($rc->getFileName()).'/'.basename($rc->getFileName(), '.php'); - } - - - function __construct(Slim $app, $db, $user) { - $this->_arg_list = array(); - $this->_dispatch = array(); - $class = $this; - while ($class) { - $this->_arg_list = array_merge($class::$arg_list, $this->_arg_list); - $this->_dispatch = array_merge($class::$dispatch, $this->_dispatch); - $class = get_parent_class($class); - } + protected $app, $db, $user, $ty; + + var $require_staff = False; + var $staff = False; + var $visits = array(); + var $debug = False; + var $explain = False; + var $stats = False; + var $profile = False; + var $profiles = array(); + var $base; + var $args = array(); + var $request = null; + + public static $dispatch = array(); + public static $arg_list = array(); + + private $generic_args = array('page' => '\d+', + 'per_page' => '\d+', + 'sort_by' => '\w+', + 'order' => 'desc|asc', + 'ty' => '\w+', + 's' => '[\w\s\-]+', + 'prop' => '\w+\d+', + ); + + + var $sessionid; + var $proposalid; + var $_arg_list; + var $_dispatch; + var $last_profile; + + function _base() + { + $rc = new ReflectionClass(get_class($this)); + return dirname($rc->getFileName()) . '/' . basename($rc->getFileName(), '.php'); + } + + + function __construct(Slim $app, DatabaseParent $db, $user) + { + $this->_arg_list = array(); + $this->_dispatch = array(); + $class = $this; + while ($class) + { + $this->_arg_list = array_merge($class::$arg_list, $this->_arg_list); + $this->_dispatch = array_merge($class::$dispatch, $this->_dispatch); + $class = get_parent_class($class); + } - $this->app = $app; - - $this->last_profile = microtime(True); - $this->db = $db; - $this->db->set_debug($this->debug); - $this->db->set_explain($this->explain); - $this->db->set_stats($this->stats); - - $this->_setup_routes(); - - $this->user = $user; - } - - - function _get_type() { - global $prop_types; - $this->ty = 'gen'; - - if ($this->user) { - $ty = 'gen'; - if ($this->has_arg('prop')) { - if (preg_match('/([A-z]+)\d+/', $this->arg('prop'), $m)) { - $prop_code = $m[1]; - - // See if proposal code matches list in config - $found = False; - foreach ($prop_types as $pty) { - if ($prop_code == $pty) { - $ty = $pty; - $found = True; - } - } - - // Proposal code didn't match, work out what beamline the visits are on - // Modified to order the results by visit number (that way any special case/session-0 visit come last) - if (!$found) { - $bls = $this->db->pq("SELECT s.beamlinename FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode,p.proposalnumber) LIKE :1 ORDER BY s.visit_number DESC", array($m[0])); - - if (sizeof($bls)) { - foreach ($bls as $bl) { - $b = $bl['BEAMLINENAME']; - - $ty = $this->_get_type_from_beamline($b); - - if ($ty) break; - } - } + $this->app = $app; + + $this->last_profile = microtime(True); + $this->db = $db; + $this->db->set_debug($this->debug); + $this->db->set_explain($this->explain); + $this->db->set_stats($this->stats); + + $this->_setup_routes(); + + $this->user = $user; + } + + + function _get_type() + { + global $prop_types; + $this->ty = 'gen'; + + if ($this->user) + { + $ty = 'gen'; + if ($this->has_arg('prop')) + { + if (preg_match('/([A-z]+)\d+/', $this->arg('prop'), $m)) + { + $prop_code = $m[1]; + + // See if proposal code matches list in config + $found = False; + foreach ($prop_types as $pty) + { + if ($prop_code == $pty) + { + $ty = $pty; + $found = True; } } - // get default type from type of admin - } - else { - foreach ($this->user->perms as $p) { - if (strpos($p, '_admin')) { - $parts = explode('_', $p); - $ty = $parts[0]; - break; + // Proposal code didn't match, work out what beamline the visits are on + // Modified to order the results by visit number (that way any special case/session-0 visit come last) + if (!$found) + { + $bls = $this->db->pq("SELECT s.beamlinename FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode,p.proposalnumber) LIKE :1 ORDER BY s.visit_number DESC", array($m[0])); + + if (sizeof($bls)) + { + foreach ($bls as $bl) + { + $b = $bl['BEAMLINENAME']; + + $ty = $this->_get_type_from_beamline($b); + + if ($ty) + break; + } } } } - // Possible we set ty to null while trying to get type from beamline - $this->ty = $ty ? $ty : 'gen'; + } + else if ($this->user) + { + $ty = $this->user->getAdminType(); + } + // Possible we set ty to null while trying to get type from beamline + $this->ty = $ty ? $ty : 'gen'; + } + } + + /** + * Return the type (group) that belongs to the passed beamline + * + * @param String $bl Beamline e.g. 'i01', 'm01', etc. + * @return String Returns beamline type/group e.g. 'mx', 'em', 'xpdf' or null if not found + */ + function _get_type_from_beamline($bl) + { + global $bl_types; + + $beamline_type = null; + + foreach ($bl_types as $type) + { + if ($type['name'] == $bl) + { + $beamline_type = $type['group']; + + break; } } - /** - * Return the type (group) that belongs to the passed beamline - * - * @param String $bl Beamline e.g. 'i01', 'm01', etc. - * @return String Returns beamline type/group e.g. 'mx', 'em', 'xpdf' or null if not found - */ - function _get_type_from_beamline($bl) { - global $bl_types; - - $beamline_type = null; + return $beamline_type; + } + + /** + * Return a list of beamlines based on the type/group (mx, em, gen) + * The return value can be checked with empty() if required + * + * @param String $ty Beamline type/group 'mx', 'em', etc. or 'all' to get all beamlines + * @param bool $archived Default: false. Flag that allows archived beamlines to be included in result + * @return Array Returns list of beamlines that are part of the beamline type + */ + function _get_beamlines_from_type($ty, $archived = False) + { + global $bl_types; + + $beamlines = array(); + + // Guard against null value passed in + if (!$ty) + return $beamlines; - foreach($bl_types as $type) { - if ($type['name'] == $bl) { - $beamline_type = $type['group']; - - break; + if ($ty == 'all') + { + $beamlines = array_filter(array_map(function ($k) use ($archived) + { + if ($archived) + { + // Then return all beamlines ignoring the archived property for each beamline + return $k['name']; } - } - - return $beamline_type; - } - - /** - * Return a list of beamlines based on the type/group (mx, em, gen) - * The return value can be checked with empty() if required - * - * @param String $ty Beamline type/group 'mx', 'em', etc. or 'all' to get all beamlines - * @param Boolean $archived Default: False. Flag that allows archived beamlines to be included in result - * @return Array Returns list of beamlines that are part of the beamline type - */ - function _get_beamlines_from_type($ty, $archived = False) { - global $bl_types; - - $beamlines = array(); - - // Guard against null value passed in - if (!$ty) return $beamlines; - - if ($ty == 'all') { - $beamlines = array_filter(array_map(function($k) use ($archived) { - if ($archived) { - // Then return all beamlines ignoring the archived property for each beamline - return $k['name']; - } else { - // We need to filter beamlines where its archived property is set to true. - // If there is no archived property set for the beamline, it's not archived - $beamlineIsArchived = array_key_exists('archived', $k) ? $k['archived'] : False; - return $beamlineIsArchived ? NULL : $k['name']; - } - }, $bl_types)); - } else { - $beamlines = array_filter(array_map(function($k) use ($ty, $archived) { + else + { + // We need to filter beamlines where its archived property is set to true. + // If there is no archived property set for the beamline, it's not archived $beamlineIsArchived = array_key_exists('archived', $k) ? $k['archived'] : False; - if ($archived && $k['group'] == $ty) { - return $k['name']; - } else if ($k['group'] == $ty && !$beamlineIsArchived) { - return $k['name']; - } - }, $bl_types)); - } - - return $beamlines; + return $beamlineIsArchived ? NULL : $k['name']; + } + }, $bl_types)); + } + else + { + $beamlines = array_filter(array_map(function ($k) use ($ty, $archived) + { + $beamlineIsArchived = array_key_exists('archived', $k) ? $k['archived'] : False; + if ($archived && $k['group'] == $ty) + { + return $k['name']; + } + else if ($k['group'] == $ty && !$beamlineIsArchived) + { + return $k['name']; + } + }, $bl_types)); } + return $beamlines; + } + + + function auth($require_staff) + { + if ($require_staff) + { + $auth = $this->staff; - function auth($require_staff) { - if ($require_staff) { - $auth = $this->staff; - // Beamline Sample Registration - } else if ($this->blsr() && !$this->user->login) { - $auth = false; - - if ($this->has_arg('visit')) { - $blsr_visits = array(); - foreach ($this->blsr_visits() as $v) array_push($blsr_visits, $v['VISIT']); - - if (in_array($this->arg('visit'), $blsr_visits)) $auth = True; - - } else { - $auth = true; - } + } + else if ($this->blsr() && !$this->user->loginId) + { + $auth = false; - // Barcode Scanners - } else if ($this->bcr() && !$this->user->login) { + if ($this->has_arg('visit')) + { + $blsr_visits = array(); + foreach ($this->blsr_visits() as $v) + array_push($blsr_visits, $v['VISIT']); + + if (in_array($this->arg('visit'), $blsr_visits)) + $auth = True; + + } + else + { $auth = true; - + } + + // Barcode Scanners + } + else if ($this->bcr() && !$this->user->loginId) + { + $auth = true; + // Normal validation - } else { - $auth = False; - - // Registered visit or staff - if ($this->staff) { - $auth = True; - - if ($this->has_arg('prop')) { - $prop = $this->db->pq('SELECT p.proposalid FROM proposal p WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1', array($this->arg('prop'))); - - if (sizeof($prop)) $this->proposalid = $prop[0]['PROPOSALID']; - } - + } + else + { + $auth = False; + + // Registered visit or staff + if ($this->staff) + { + $auth = True; + + if ($this->has_arg('prop')) + { + $prop = $this->db->pq('SELECT p.proposalid FROM proposal p WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1', array($this->arg('prop'))); + + if (sizeof($prop)) + $this->proposalid = $prop[0]['PROPOSALID']; + } + // Normal users - } else { - $rows = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis + } + else + { + $rows = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis FROM proposal p INNER JOIN blsession s ON p.proposalid = s.proposalid INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid - WHERE shp.personid=:1", array($this->user->personid)); + WHERE shp.personid=:1", array($this->user->personId)); - foreach ($rows as $row) { - array_push($this->visits, strtolower($row['VIS'])); - } - - /*$ids = $this->db->pq("SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalcode || p.proposalnumber || '-' || s.visit_number in ('".implode("','", $this->visits)."')"); - - $this->sessionids = array(); - foreach ($ids as $id) { - array_push($this->sessionids, $id['SESSIONID']); - }*/ - #print_r($this->sessionids); - - if ($this->has_arg('id') || $this->has_arg('visit') || $this->has_arg('prop')) { - - // Check user is in this visit - if ($this->has_arg('id')) { - $types = array('data' => array('datacollectiongroup', 'datacollectionid'), - 'edge' => array('energyscan', 'energyscanid'), - 'mca' => array('xfefluorescencespectrum', 'xfefluorescencespectrumid'), - ); - - $table = 'datacollectiongroup'; - $col = 'datacollectionid'; - if ($this->has_arg('t')) { - if (array_key_exists($this->arg('t'), $types)) { - $table = $types[$this->arg('t')][0]; - $col = $types[$this->arg('t')][1]; - } + foreach ($rows as $row) + { + array_push($this->visits, strtolower($row['VIS'])); + } + + /*$ids = $this->db->pq("SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalcode || p.proposalnumber || '-' || s.visit_number in ('".implode("','", $this->visits)."')"); + + $this->sessionids = array(); + foreach ($ids as $id) { + array_push($this->sessionids, $id['SESSIONID']); + }*/ + #print_r($this->sessionids); + + if ($this->has_arg('id') || $this->has_arg('visit') || $this->has_arg('prop')) + { + + // Check user is in this visit + if ($this->has_arg('id')) + { + $types = array('data' => array('datacollectiongroup', 'datacollectionid'), + 'edge' => array('energyscan', 'energyscanid'), + 'mca' => array('xfefluorescencespectrum', 'xfefluorescencespectrumid'), + ); + + $table = 'datacollectiongroup'; + $col = 'datacollectionid'; + if ($this->has_arg('t')) + { + if (array_key_exists($this->arg('t'), $types)) + { + $table = $types[$this->arg('t')][0]; + $col = $types[$this->arg('t')][1]; } - - if ($table == 'datacollectiongroup') { - $vis = $this->db->pq("SELECT p.proposalid, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis + } + + if ($table == 'datacollectiongroup') + { + $vis = $this->db->pq("SELECT p.proposalid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN datacollectiongroup dcg ON s.sessionid = dcg.sessionid INNER JOIN datacollection dc ON dcg.datacollectiongroupid = dc.datacollectiongroupid WHERE dc.datacollectionid = :1", array($this->arg('id'))); - } else { - $vis = $this->db->pq("SELECT p.proposalid, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis + } + else + { + $vis = $this->db->pq("SELECT p.proposalid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN $table dc ON s.sessionid = dc.sessionid WHERE dc.$col = :1", array($this->arg('id'))); - } + } + + if (sizeof($vis)) + $this->proposalid = $vis[0]['PROPOSALID']; + $vis = sizeof($vis) ? $vis[0]['VIS'] : ''; + + + } + else if ($this->has_arg('visit')) + { + $vis = $this->arg('visit'); + + $visp = $this->db->pq("SELECT p.proposalid FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1", array($this->arg('visit'))); + + if (sizeof($visp)) + $this->proposalid = $visp[0]['PROPOSALID']; - if (sizeof($vis)) $this->proposalid = $vis[0]['PROPOSALID']; - $vis = sizeof($vis) ? $vis[0]['VIS'] : ''; - - - } else if ($this->has_arg('visit')) { - $vis = $this->arg('visit'); - - $visp = $this->db->pq("SELECT p.proposalid FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1", array($this->arg('visit'))); - - if (sizeof($visp)) $this->proposalid = $visp[0]['PROPOSALID']; - // Check user is in this proposal - } else if ($this->has_arg('prop')) { - $viss = $this->db->pq("SELECT p.proposalid, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1", array($this->arg('prop'))); - - $vis = array(); - foreach ($viss as $v) array_push($vis, $v['VIS']); - if (sizeof($viss)) $this->proposalid = $viss[0]['PROPOSALID']; - } - - if ($this->has_arg('id') || $this->has_arg('visit')) { - if (in_array($vis, $this->visits)) $auth = True; - } else { - if (sizeof(array_intersect($vis, $this->visits))) $auth = True; - } - - // No id or visit, anyone ok to view - } else { - $auth = True; } - } - } + else if ($this->has_arg('prop')) + { + $viss = $this->db->pq("SELECT p.proposalid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1", array($this->arg('prop'))); + + $vis = array(); + foreach ($viss as $v) + array_push($vis, $v['VIS']); + if (sizeof($viss)) + $this->proposalid = $viss[0]['PROPOSALID']; + } - - // End execution, show not authed page template - if (!$auth) { - $this->_error('Access Denied', 'You dont have access to that page'); + if ($this->has_arg('id') || $this->has_arg('visit')) + { + if (in_array($vis, $this->visits)) + $auth = True; + } + else + { + if (sizeof(array_intersect($vis, $this->visits))) + $auth = True; + } + + // No id or visit, anyone ok to view + } + else + { + $auth = True; + } } - - return $auth; } - - function _setup_routes() { - foreach ($this->_dispatch as $args) { - if (sizeof($args) > 4) $this->app->{$args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($args[3])->name($args[4]); - if (sizeof($args) > 3) $this->app->{$args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($args[3]); - else $this->app->{$args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($this->_arg_list); - } + // End execution, show not authed page template + if (!$auth) + { + $this->_error('Access Denied', 'You dont have access to that page'); } - - function execute($route) { - $this->_parse_args(); - $this->_get_type(); - $this->staff = $this->user->has($this->ty.'_admin'); - if (in_array($route->getName(), array('edge', 'mca'))) { - $this->args['t'] = $route->getName(); - } + return $auth; + } - /*$extra = $route->getParams(); - foreach (array('id', 'visit', 't') as $i => $k) { - if (array_key_exists($k, $extra)) $this->args[$k] = $extra[$k]; - }*/ - $extra = array(); - foreach ($route->getParams() as $k => $v) { - if ($v) $extra[$k] = $v; - } - $this->args = array_merge($this->args, $extra); + function _setup_routes() + { + foreach ($this->_dispatch as $args) + { + if (sizeof($args) > 4) + $this->app->{ $args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($args[3])->name($args[4]); + if (sizeof($args) > 3) + $this->app->{ $args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($args[3]); + else + $this->app->{ $args[1]}($args[0], array(&$this, 'execute'), array(&$this, $args[2]))->conditions($this->_arg_list); + } + } - $ret = $this->auth($this->require_staff, $this); + function execute($route) + { + $this->_parse_args(); + $this->_get_type(); + $this->staff = $this->user->hasPermission($this->ty . '_admin'); - return $ret; + if (in_array($route->getName(), array('edge', 'mca'))) + { + $this->args['t'] = $route->getName(); } - - - # ------------------------------------------------------------------------ - # Log Action - function log_action($act=1,$com='') { - if (get_class($this) == 'Image') return; - if (get_class($this) == 'Download') return; - - $action = $act ? 'LOGON' : 'LOGOFF'; - - if ($this->user) { - $com = 'ISPyB2: '.($com ? $com : $_SERVER['REQUEST_URI']); - $chk = $this->db->pq("SELECT comments FROM adminactivity WHERE username LIKE :1", array($this->user->login)); - - if (sizeof($chk)) { - $this->db->pq("UPDATE adminactivity SET action=:1, comments=:2, datetime=SYSDATE WHERE username=:3", array($action, $com, $this->user->login)); - - - } else { - $this->db->pq("INSERT INTO adminactivity (adminactivityid, username, action, comments, datetime) VALUES (s_adminactivity.nextval, :1, :2, :3, SYSDATE)", array($this->user->login, $action, $com)); - } - + + $extra = array(); + foreach ($route->getParams() as $k => $v) + { + if ($v) + $extra[$k] = $v; + } + $this->args = array_merge($this->args, $extra); + + $ret = $this->auth($this->require_staff, $this); + + return $ret; + } + + + # ------------------------------------------------------------------------ + # Log Action + function log_action($act = 1, $com = '') + { + if (get_class($this) == 'Image') + return; + if (get_class($this) == 'Download') + return; + + $action = $act ? 'LOGON' : 'LOGOFF'; + + if (Utils::ShouldLogUserActivityToDB($this->user->loginId)) + { + $com = 'ISPyB2: ' . ($com ? $com : $_SERVER['REQUEST_URI']); + $chk = $this->db->pq("SELECT comments FROM adminactivity WHERE username LIKE :1", array($this->user->loginId)); + + if (sizeof($chk)) + { + $this->db->pq("UPDATE adminactivity SET action=:1, comments=:2, datetime=SYSDATE WHERE username=:3", array($action, $com, $this->user->loginId)); } - - return true; - } - - - # ------------------------------------------------------------------------ - # Output JSON encoded data - function _output($data=array()) { - if (!$this->debug && !$this->db->debug) $this->app->contentType('application/json'); - if ($this->profile) $data['profile'] = $this->pro(); - $this->app->response()->body(json_encode($data)); - //print json_encode($data); - if ($this->explain) print "\n".$this->db->plan; - if ($this->db->stats) print "\n".$this->db->stat; - - } - - # Error messages as json object, should probably return a different - # http code as well - function _error($msg, $code=400) { - //header('HTTP/1.1 400 Bad Request'); - //header('Content-type:application/json'); - //print - - $this->app->halt($code, json_encode(array('status' => $code, 'message' => $msg))); - //exit(); - } - - - - # ------------------------------------------------------------------------ - # Convert input arg url to key / value pairs once checked against templates - //function _parse_args($args) { - function _parse_args() { - // Set the cache dir to a temp folder - $serializer_temp = sys_get_temp_dir() . "/htmlpurifier/"; - $config = HTMLPurifier_Config::createDefault(); - if(!is_dir($serializer_temp)) { - mkdir($serializer_temp); + else + { + $this->db->pq("INSERT INTO adminactivity (adminactivityid, username, action, comments, datetime) VALUES (s_adminactivity.nextval, :1, :2, :3, SYSDATE)", array($this->user->loginId, $action, $com)); } - $config->set('Cache.SerializerPath', $serializer_temp); - $purifier = new HTMLPurifier($config); - - $bbreq = (array)json_decode($this->app->request()->getBody()); - $request = array_merge($_REQUEST, $bbreq); - $this->request = $request; - - $parsed = array(); - - // Array of arguments - if (sizeof($request) && !$this->is_assoc($request)) { - $pa = array(); - foreach ($request as $r) { - $par = array(); - foreach (array_merge($this->generic_args, $this->_arg_list) as $k => $v) { - if (array_key_exists($k, $r)) { - - if (is_array($r->$k)) { - $tmp = array(); - foreach ($r->$k as $val) { - if (preg_match('/^'.$v.'$/m', $val)) { - array_push($tmp, $v == '.*' ? $purifier->purify($val) : $val); - } - } - $par[$k] = $tmp; - - } else { - if (preg_match('/^'.$v.'$/m', $r->$k)) { - $par[$k] = $v == '.*' ? $purifier->purify($r->$k) : $r->$k; - if ($k == 'prop') $parsed[$k] = $par[$k]; + } + return true; + } + + + # ------------------------------------------------------------------------ + # Output JSON encoded data + function _output($data = array()) + { + if (!$this->debug && !$this->db->debug) + $this->app->contentType('application/json'); + if ($this->profile) + $data['profile'] = $this->pro(); + $this->app->response()->body(json_encode($data)); + if ($this->explain) + print "\n" . $this->db->plan; + if ($this->db->stats) + print "\n" . $this->db->stat; + } + + # Error messages as json object, default status code of 400 + function _error($msg, $code = 400) + { + $this->app->halt($code, json_encode(array('status' => $code, 'message' => $msg))); + } + + + + # ------------------------------------------------------------------------ + # Convert input arg url to key / value pairs once checked against templates + //function _parse_args($args) { + function _parse_args() + { + // Set the cache dir to a temp folder + $serializer_temp = sys_get_temp_dir() . "/htmlpurifier/"; + $config = HTMLPurifier_Config::createDefault(); + if (!is_dir($serializer_temp)) + { + mkdir($serializer_temp); + } + $config->set('Cache.SerializerPath', $serializer_temp); + $purifier = new HTMLPurifier($config); + + $bbreq = (array) json_decode($this->app->request()->getBody()); + $request = array_merge($_REQUEST, $bbreq); + $this->request = $request; + + $parsed = array(); + + // Array of arguments + if (sizeof($request) && !$this->is_assoc($request)) + { + $pa = array(); + foreach ($request as $r) + { + $par = array(); + foreach (array_merge($this->generic_args, $this->_arg_list) as $k => $v) + { + if (property_exists($r, $k) && isset($r->$k)) + { + if (is_array($r->$k)) + { + $tmp = array(); + foreach ($r->$k as $val) + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $val)) + { + array_push($tmp, $v == '.*' ? $purifier->purify($val) : $val); } } + $par[$k] = $tmp; + + } + else + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $r->$k)) + { + $par[$k] = $v == '.*' ? $purifier->purify($r->$k) : $r->$k; + if ($k == 'prop') + $parsed[$k] = $par[$k]; + } } } - array_push($pa, $par); } - - $parsed['collection'] = $pa; - } else { - foreach (array_merge($this->generic_args, $this->_arg_list) as $k => $v) { - if (!array_key_exists($k, $parsed)) { - if (array_key_exists($k, $request)) { - if (is_array($request[$k])) { - $tmp = array(); - foreach ($request[$k] as $val) { - if (preg_match('/^'.$v.'$/m', $val)) { - array_push($tmp, $v == '.*' ? $purifier->purify($val) : $val); - } + array_push($pa, $par); + } + + $parsed['collection'] = $pa; + } + else + { + foreach (array_merge($this->generic_args, $this->_arg_list) as $k => $v) + { + if (!array_key_exists($k, $parsed)) + { + if (array_key_exists($k, $request)) + { + if (is_array($request[$k])) + { + $tmp = array(); + foreach ($request[$k] as $val) + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $val)) + { + array_push($tmp, $v == '.*' ? $purifier->purify($val) : $val); } - $parsed[$k] = $tmp; - - } elseif ($request[$k] instanceof stdClass) { - // Handles nested backbone models - foreach($request[$k] as $key => $value) { - if(is_array($value)) { - $tmp = array(); - foreach ($value as $value2) { - if (preg_match('/^'.$v.'$/m', $value2)) { - array_push($tmp, $v == '.*' ? $purifier->purify($value2) : $value2); - } - } - $parsed[$k] = $tmp; - } else { - if(preg_match('/^'.$v.'$/m', $value)) { - $request[$k]->$key = $v == '.*' ? $purifier->purify($value) : $value; + } + $parsed[$k] = $tmp; + + } + elseif ($request[$k] instanceof \stdClass) + { + // Handles nested backbone models + foreach ($request[$k] as $key => $value) + { + if (is_array($value)) + { + $tmp = array(); + foreach ($value as $value2) + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $value2)) + { + array_push($tmp, $v == '.*' ? $purifier->purify($value2) : $value2); } } + $parsed[$k] = $tmp; } - $parsed[$k] = $request[$k]; - - } elseif($k == 'json') { - // Handles nested backbone models when submitted with files - // Necessary due to multi content-type form data requiring models to be submitted together as a single JSON string - $json = json_decode($request[$k]); - - foreach($json as $label => $object){ - if($object instanceof stdClass){ - foreach($object as $name => $item){ - if(is_array($item)) { - $tmp = array(); - foreach ($item as $element) { - if (preg_match('/^'.$v.'$/m', $element)) { - array_push($tmp, $v == '.*' ? $purifier->purify($element) : $element); - } - } - $object->$name = $tmp; - } else { - if(preg_match('/^'.$v.'$/m', $item)) { - $object->$name = $v == '.*' ? $purifier->purify($item) : $item; + else + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $value)) + { + $request[$k]->$key = $v == '.*' ? $purifier->purify($value) : $value; + } + } + } + $parsed[$k] = $request[$k]; + + } + elseif ($k == 'json') + { + // Handles nested backbone models when submitted with files + // Necessary due to multi content-type form data requiring models to be submitted together as a single JSON string + $json = json_decode($request[$k]); + + foreach ($json as $label => $object) + { + if ($object instanceof \stdClass) + { + foreach ($object as $name => $item) + { + if (is_array($item)) + { + $tmp = array(); + foreach ($item as $element) + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $element)) + { + array_push($tmp, $v == '.*' ? $purifier->purify($element) : $element); } } + $object->$name = $tmp; + } + else + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $item)) + { + $object->$name = $v == '.*' ? $purifier->purify($item) : $item; + } } } - $parsed[$label] = $object; - } - } else { - if (preg_match('/^'.$v.'$/m', $request[$k])) { - $parsed[$k] = $v == '.*' ? $purifier->purify($request[$k]) : $request[$k]; } + $parsed[$label] = $object; + } + } + else + { + if ($this->_match_pattern_to_input('/^' . $v . '$/m', $request[$k])) + { + $parsed[$k] = $v == '.*' ? $purifier->purify($request[$k]) : $request[$k]; } } } } } - - # Retrieve cookie args - // if ($this->user) { - // if (array_key_exists('ispyb_prop_'.$this->user->login, $_COOKIE) && !array_key_exists('prop', $parsed)) $parsed['prop'] = $_COOKIE['ispyb_prop_'.$this->user->login]; - // } - $this->args = $parsed; - } - - - # ------------------------------------------------------------------------ - # Nice interface to args - function has_arg($key) { - return array_key_exists($key, $this->args); - } - - function arg($key) { - if (!$this->has_arg($key)) new \Exception(); - return $this->args[$key]; - } - - function def_arg($key, $val) { - return $this->has_arg($key) ? $this->arg($key) : $val; - } - - # ------------------------------------------------------------------------ - # Misc Helpers - - # Pretty-ish printer - function p($array) { - if ($this->debug) { - print '

Debug

';
-                print_r($array);
-                print '
'; - } } - - # Unix time to javascript timestamp - function jst($str, $plus=True) { - return strtotime($str.' GMT')*1000;# + ($plus ? (3600*1000) : 0); - } - - - # Get a PV - function pv($pvs, $full=false, $string=false) { - global $bl_pv_prog, $bl_pv_env; - putenv($bl_pv_env); - exec($bl_pv_prog . ($string ? ' -S' : '') .' ' . implode(' ', $pvs) . ' 2>/dev/null', $ret); - $output = array(); - foreach ($ret as $i => $v) { - $lis = preg_split('/\s+/', $v, 2); - $output[$lis[0]] = sizeof($lis) > 1 ? ($full ? array_slice($lis,1) : $lis[1]) : ''; - } - - return $output; - } - - - # Check for trailing slash on path - function ads($var) { - if (!(substr($var, -1, 1) == '/')) $var .= '/'; - return $var; - } - - - function dirs($root) { - $d = array(); - - if (file_exists($root)) { - foreach (scandir($root) as $f) { - if ($f === '.' or $f === '..') continue; - if (is_dir($root.'/'.$f)) array_push($d,$f); - } - } - - return $d; - } - - - # ------------------------------------------------------------------------ - # Page profiling, call with a message to log the time taken between calls - function profile($msg) { - if ($this->profile) - array_push($this->profiles, $msg.': '.(microtime(True) - $this->last_profile)); - $this->last_profile = microtime(True); - } - - function pro() { - return $this->profiles; - } - - - # ------------------------------------------------------------------------ - # Beamline sample registration: Get Beamline from IP - function ip2bl() { - global $ip2bl; - $parts = explode('.', $_SERVER['REMOTE_ADDR']); - - if (array_key_exists($parts[2], $ip2bl)) { - return $ip2bl[$parts[2]]; - } + $this->args = $parsed; + } + + + # ------------------------------------------------------------------------ + # Nice interface to args + function has_arg($key) + { + return array_key_exists($key, $this->args); + } + + function arg($key) + { + if (!$this->has_arg($key)) + throw new Exception("Missing propery: " . $key); + return $this->args[$key]; + } + + function def_arg($key, $val) + { + return $this->has_arg($key) ? $this->arg($key) : $val; + } + + function argOrEmptyString($key) + { + return $this->has_arg($key) ? $this->arg($key) : ''; + } + + function argOrNull($key) + { + return $this->has_arg($key) ? $this->arg($key) : null; + } + + # ------------------------------------------------------------------------ + # Misc Helpers + + /** + * Replicates preg_match but captures the case where the input can not be converted to string + */ + function _match_pattern_to_input($pattern, $input) + { + if (is_null($input) || is_scalar($input) || (is_object($input) && method_exists($input, '__toString'))) { + return preg_match($pattern, strval($input)); + } + return false; + } + + # Pretty-ish printer + function p($array) + { + if ($this->debug) + { + print '

Debug

';
+            print_r($array);
+            print '
'; } - - - # Return visit list for blsr; - function blsr_visits() { - $b = $this->ip2bl(); - - if (!$b) return array(); - - $visits = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) < 1 AND TIMESTAMPDIFF('DAY', CURRENT_TIMESTAMP, s.enddate) < 2 AND s.beamlinename LIKE :1 ORDER BY s.startdate", array($b)); - - //if (!sizeof($visits)) { - $v = $this->db->paginate("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE p.proposalcode LIKE 'cm' AND s.beamlinename LIKE :1 AND s.enddate <= CURRENT_TIMESTAMP ORDER BY s.startdate DESC", array($b,0,1)); - $visits = array_merge($visits, $v); - //} - - return $visits; - } - - # Beamline Sample Registration Machine - function blsr() { - global $blsr; - - return in_array($_SERVER['REMOTE_ADDR'], $blsr); - } - - # Barcode Scanner Machines - function bcr() { - global $bcr; - return in_array($_SERVER['REMOTE_ADDR'], $bcr); - } - - - # ------------------------------------------------------------------------ - # LDAP: Return a name for a fedid - /** - * Search LDAP for name of user via fedid - * - * @param string $fedid "abc12345" - * @return string Returns name (givenname surname) if found - */ - function _get_name($fedid) { - $src = $this->_ldap_search('uid='.$fedid); - return array_key_exists($fedid, $src) ? $src[$fedid] : ''; - } - - /** - * Search LDAP for 'mail' property via fedid - * - * @param string $fedid "abc12345" - * @return string Returns email address if found, fedid if not. (Not all entries have mail records) - */ - function _get_email($fedid) { - $src = $this->_ldap_search('uid='.$fedid, True); - return array_key_exists($fedid, $src) ? $src[$fedid] : $fedid; - } - - /** - * Search LDAP for 'mail' property - * - * @param string $name "Firstname Surname" or "Title Firstname Surname" - * @return string Returns email address if found - */ - function _get_email_fn($name) { - $parts = explode(' ', $name); - if (sizeof($parts) == 2) { - $fn = $parts[0]; - $ln = $parts[1]; - } else if (sizeof($parts) == 3) { - $fn = $parts[1]; - $ln = $parts[2]; - } else return; - - $src = $this->_ldap_search("(&(sn=$ln)(givenname=$fn))", true); - - $ret = array(); - foreach ($src as $fedid => $email) { - array_push($ret, $email); + } + + # Unix time to javascript timestamp + function jst($str, $plus = True) + { + return strtotime($str . ' GMT') * 1000; # + ($plus ? (3600*1000) : 0); + } + + + # Get a PV + function pv($pvs, $full = false, $string = false) + { + global $bl_pv_prog, $bl_pv_env; + putenv($bl_pv_env); + exec($bl_pv_prog . ($string ? ' -S' : '') . ' ' . implode(' ', $pvs) . ' 2>/dev/null', $ret); + $output = array(); + foreach ($ret as $i => $v) + { + $lis = preg_split('/\s+/', $v, 2); + $output[$lis[0]] = sizeof($lis) > 1 ? ($full ? array_slice($lis, 1) : $lis[1]) : ''; + } + return $output; + } + + + # Check for trailing slash on path + function ads($var) + { + if (!(substr($var, -1, 1) == '/')) + $var .= '/'; + return $var; + } + + + function dirs($root) + { + $d = array(); + + if (file_exists($root)) + { + foreach (scandir($root) as $f) + { + if ($f === '.' or $f === '..') + continue; + if (is_dir($root . '/' . $f)) + array_push($d, $f); } + } - if (sizeof($ret)) return $ret[0]; - } - /** - * Search ISPyB Person record for 'emailAddress' property - * This limits the search to staff users i.e. those with at least one usergroup association - * Its used when an LDAP lookup fails for a local contact - * - * @param string $name "Firstname Surname" or "Title Firstname Surname" - * @return string Returns email address if found - */ - function _get_ispyb_email_fn($name) { - $email = ''; - $fn = ''; - $ln = ''; - - $parts = explode(' ', $name); - if (sizeof($parts) == 2) { - $fn = $parts[0]; - $ln = $parts[1]; - } else if (sizeof($parts) == 3) { - $fn = $parts[1]; - $ln = $parts[2]; - } - if ($fn && $ln) { - // Try finding an email address from within ISPyB - // We are only interested in staff users so join with usergroup table - $lc_emails = $this->db->pq("SELECT pe.emailaddress + return $d; + } + + + # ------------------------------------------------------------------------ + # Page profiling, call with a message to log the time taken between calls + function profile($msg) + { + if ($this->profile) + array_push($this->profiles, $msg . ': ' . (microtime(True) - $this->last_profile)); + $this->last_profile = microtime(True); + } + + function pro() + { + return $this->profiles; + } + + + # ------------------------------------------------------------------------ + # Beamline sample registration: Get Beamline from IP + function ip2bl() + { + global $ip2bl; + $parts = explode('.', $_SERVER['REMOTE_ADDR']); + + if ($parts && sizeof($parts) > 1 && array_key_exists($parts[2], $ip2bl)) + { + return $ip2bl[$parts[2]]; + } + } + + + # Return visit list for blsr; + function blsr_visits() + { + $b = $this->ip2bl(); + + if (!$b) + return array(); + + $visits = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) < 1 AND TIMESTAMPDIFF('DAY', CURRENT_TIMESTAMP, s.enddate) < 2 AND s.beamlinename LIKE :1 ORDER BY s.startdate", array($b)); + $v = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE p.proposalcode LIKE 'cm' AND s.beamlinename LIKE :1 AND s.enddate <= CURRENT_TIMESTAMP ORDER BY s.startdate DESC", array($b, 0, 1)); + $visits = array_merge($visits, $v); + return $visits; + } + + # Beamline Sample Registration Machine + function blsr() + { + global $blsr; + + return in_array($_SERVER['REMOTE_ADDR'], $blsr); + } + + # Barcode Scanner Machines + function bcr() + { + global $bcr; + return in_array($_SERVER['REMOTE_ADDR'], $bcr); + } + + + # ------------------------------------------------------------------------ + # LDAP: Return a name for a fedid + /** + * Search LDAP for name of user via fedid + * + * @param string $fedid "abc12345" + * @return string Returns name (givenname surname) if found + */ + function _get_name($fedid) + { + $src = $this->_ldap_search('uid=' . $fedid); + return array_key_exists($fedid, $src) ? $src[$fedid] : ''; + } + + /** + * Search LDAP for 'mail' property via fedid + * + * @param string $fedid "abc12345" + * @return string Returns email address if found, fedid if not. (Not all entries have mail records) + */ + function _get_email($fedid) + { + $src = $this->_ldap_search('uid=' . $fedid, True); + return array_key_exists($fedid, $src) ? $src[$fedid] : $fedid; + } + + /** + * Search LDAP for 'mail' property + * + * @param string $name "Firstname Surname" or "Title Firstname Surname" + * @return string Returns email address if found + */ + function _get_email_fn($name) + { + $parts = explode(' ', $name); + if (sizeof($parts) == 2) + { + $fn = $parts[0]; + $ln = $parts[1]; + } + else if (sizeof($parts) == 3) + { + $fn = $parts[1]; + $ln = $parts[2]; + } + else + return; + + $src = $this->_ldap_search("(&(sn=$ln)(givenname=$fn))", true); + + $ret = array(); + foreach ($src as $fedid => $email) + { + array_push($ret, $email); + } + + if (sizeof($ret)) + return $ret[0]; + } + /** + * Search ISPyB Person record for 'emailAddress' property + * This limits the search to staff users i.e. those with at least one usergroup association + * Its used when an LDAP lookup fails for a local contact + * + * @param string $name "Firstname Surname" or "Title Firstname Surname" + * @return string Returns email address if found + */ + function _get_ispyb_email_fn($name) + { + $email = ''; + $fn = ''; + $ln = ''; + + $parts = explode(' ', $name); + if (sizeof($parts) == 2) + { + $fn = $parts[0]; + $ln = $parts[1]; + } + else if (sizeof($parts) == 3) + { + $fn = $parts[1]; + $ln = $parts[2]; + } + if ($fn && $ln) + { + // Try finding an email address from within ISPyB + // We are only interested in staff users so join with usergroup table + $lc_emails = $this->db->pq("SELECT pe.emailaddress FROM person pe INNER JOIN usergroup_has_person ugp ON ugp.personid = pe.personid WHERE pe.givenname =:1 AND pe.familyname =:2 AND pe.emailaddress IS NOT NULL", array($fn, $ln)); - if (sizeof($lc_emails)) $email = $lc_emails[0]['EMAILADDRESS']; - } - return $email; - } - - - # Run an ldap search - /** - * Search LDAP for name or email - * - * @param boolean $email Search for an email adddress if true, search for name if false - * @param string $search ldap query, typically uid=fedid or name search - * @return array Returns array of results, either fedid=>emailAddresses or fedid=>"givenname sn" from ldap records - */ - function _ldap_search($search,$email=False) { - global $ldap_server, $ldap_search; - - $ret = array(); - $ds=ldap_connect($ldap_server); - if ($ds) { - // Explictly set the protocol version to prevent bind errors - ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); - $r=ldap_bind($ds); - $sr=ldap_search($ds, $ldap_search, $search); - $info = ldap_get_entries($ds, $sr); - - for ($i=0; $i<$info["count"]; $i++) { - // Strictly speaking we could set anything as the key here, since only the first record is used in e.g. _get_email_fn - // But as the logic maps fedid=>email, use similar keys here - $fedid = $info[$i]['uid'][0]; - if ($email) { - $ret[$fedid] = array_key_exists('mail', $info[$i]) ? $info[$i]['mail'][0] : ''; - } else $ret[$fedid] = $info[$i]['givenname'][0].' '.$info[$i]['sn'][0]; - } - - ldap_close($ds); - } - return $ret; + if (sizeof($lc_emails)) + $email = $lc_emails[0]['EMAILADDRESS']; } - - - // Deprecated - now set in javascript - # ------------------------------------------------------------------------ - # Set cookie for current proposal - function cookie($val) { - if ($this->user) { - setcookie('ispyb_prop_'.$this->user, $val, time()+31536000, '/'); - } + return $email; + } + + + # Run an ldap search + /** + * Search LDAP for name or email + * + * @param boolean $email Search for an email adddress if true, search for name if false + * @param string $search ldap query, typically uid=fedid or name search + * @return array Returns array of results, either fedid=>emailAddresses or fedid=>"givenname sn" from ldap records + */ + function _ldap_search($search, $email = False) + { + global $ldap_server, $ldap_search; + + $ret = array(); + if (is_null($ldap_server)) { + error_log("Ldap server is not configured, not looking up user."); + return $ret; } - - - # ------------------------------------------------------------------------ - # Talk to channel archiver to get a pv - function _get_archive($pv, $s, $e, $n=100) { - global $timezone; - - $m = new xmlrpcmsg('archiver.values', array( - new xmlrpcval(1000, 'int'), - new xmlrpcval(array(new xmlrpcval($pv,'string')), 'array'), - new xmlrpcval($s,'int'), - new xmlrpcval(0,'int'), - new xmlrpcval($e,'int'), - new xmlrpcval(0,'int'), - new xmlrpcval($n,'int'), - new xmlrpcval(0,'int'), - )); - $c = new xmlrpc_client("/archive/cgi/ArchiveDataServer.cgi", "archiver.pri.diamond.ac.uk", 80); - - $r = $c->send($m); - $val = $r->value(); - - if ($val) { - $str = $val->arrayMem(0); - $vals = $str->structMem('values'); - - $ret = array(); - for ($i = 0; $i < $vals->arraySize(); $i++) { - $vs = $vals->arrayMem($i); - $v = $vs->structMem('value')->arrayMem(0)->scalarVal(); - $t = $vs->structMem('secs')->scalarVal()-3600; - - $inputTZ = new \DateTimeZone($timezone); - $transitions = $inputTZ->getTransitions($t, $t); - if ($transitions[0]['isdst']) $t += 3600; - - array_push($ret, array($t,$v)); + $ds = ldap_connect($ldap_server); + if ($ds) + { + // Explictly set the protocol version to prevent bind errors + /** + * @psalm-suppress UndefinedConstant + */ + ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); + $r = ldap_bind($ds); + $sr = ldap_search($ds, $ldap_search, $search); + $info = ldap_get_entries($ds, $sr); + + for ($i = 0; $i < $info["count"]; $i++) + { + // Strictly speaking we could set anything as the key here, since only the first record is used in e.g. _get_email_fn + // But as the logic maps fedid=>email, use similar keys here + $fedid = $info[$i]['uid'][0]; + if ($email) + { + $ret[$fedid] = array_key_exists('mail', $info[$i]) ? $info[$i]['mail'][0] : ''; } - - return $ret; + else + $ret[$fedid] = $info[$i]['givenname'][0] . ' ' . $info[$i]['sn'][0]; } - } - - - function is_assoc(array $array) { - $keys = array_keys($array); - return array_keys($keys) !== $keys; + ldap_close($ds); } + return $ret; + } + + + # ------------------------------------------------------------------------ + # Talk to channel archiver to get a pv + function _get_archive($pv, $s, $e) + { + global $timezone; + global $archive_url; + + if (empty($archive_url)) + return array(); + + $parameters = array( + 'pv' => $pv, + 'from' => date("Y-m-d\TH:i:s", $s).'Z', + 'to' => date("Y-m-d\TH:i:s", $e).'Z', + ); + + $ret = array(); + $url_query = http_build_query($parameters); + $archive_query_url = $archive_url . "?". $url_query; + $val = file_get_contents($archive_query_url); + + if ($val) + { + $str = json_decode($val)[0]; + $vals = $str->data; + + for ($i = 0; $i < sizeof($vals); $i++) + { + $vs = $vals[$i]; + $v = $vs->val; + $t = $vs->secs - 3600; + $inputTZ = new \DateTimeZone($timezone); + $transitions = $inputTZ->getTransitions($t, $t); + if ($transitions[0]['isdst']) + $t += 3600; + array_push($ret, array($t, $v)); + } + } + return $ret; + } - # ------------------------------------------------------------------------ - # Make a cURL request - function _curl($options) { - $ch = curl_init(); - - $headers = getallheaders(); - if (array_key_exists('Authorization', $headers) && array_key_exists('jwt', $options)) { - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: '.$headers['Authorization'])); - } + function is_assoc(array $array) + { + $keys = array_keys($array); + return array_keys($keys) !== $keys; + } - $data = ''; - if ($options['data']) { - $data = '?'.http_build_query($options['data']); - } - curl_setopt($ch, CURLOPT_URL, $options['url'].$data); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, 0); - $content = curl_exec($ch); - $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); + # ------------------------------------------------------------------------ + # Make a cURL request + function _curl($options) + { + $ch = curl_init(); - return array('code' => $code, 'content' => $content); + curl_setopt($ch, CURLOPT_HEADER, array_key_exists('HEADER', $options) ? 1 : 0); + if (array_key_exists('HEADERS', $options)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $options['HEADERS']); } + $headers = getallheaders(); + if (array_key_exists('Authorization', $headers) && array_key_exists('jwt', $options)) + { + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: ' . $headers['Authorization'])); + } + if (array_key_exists('POST', $options)) curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + if (array_key_exists('FIELDS', $options)) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($options['FIELDS'])); - - # ------------------------------------------------------------------------ - # Page start, end, order - function _get_start_end(&$args, $default=15) { - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : $default; - $start = 0; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - array_push($args, $start); - array_push($args, $end); - } - - function _get_order($cols, $default) { - if ($this->has_arg('sort_by')) { - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) return $cols[$this->arg('sort_by')].' '.$dir; - } else return $default; - } - - - /** - * Determine processed dir path based on data collection results - * This function needs to find the samples and investigators for this container - * - * @param array $dc Array of data collection variables, must container VIS, DIR, IMP and RUN - * @param string $location directory nane to append to visit directory that holds processed results - * @return string Returns processed directory path (falls back to legacy dir with extra underscore) - */ - function get_visit_processed_dir($dc, $location) { - $root = preg_replace( '/' . $dc['VIS'] . '/', $dc['VIS'] . $location, $dc['DIR'], 1) . $dc['IMP'] . '_' . $dc['RUN'] . '/'; - if (!is_dir($root)) { - $root = preg_replace( '/' . $dc['VIS'] . '/', $dc['VIS'] . $location, $dc['DIR'], 1) . $dc['IMP'] . '_' . $dc['RUN'] . '_' . '/'; - } - return $root; + $data = ''; + if (array_key_exists('data', $options)) + { + $data = '?' . http_build_query($options['data']); } + curl_setopt($ch, CURLOPT_URL, $options['url'] . $data); - function _submit_zocalo_recipe($recipe, $parameters, $error_code=500) { - global $zocalo_mx_reprocess_queue; + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + $content = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); - if (isset($zocalo_mx_reprocess_queue)) { - // Send job to processing queue - $zocalo_message = array( - 'recipes' => array( - $recipe, - ), - 'parameters' => $parameters, - ); - $this->_send_zocalo_message($zocalo_mx_reprocess_queue, $zocalo_message, $error_code); - } - } + return array('code' => $code, 'content' => $content); + } - function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code=500) { - global - $zocalo_server, - $zocalo_username, - $zocalo_password; + # ------------------------------------------------------------------------ + # Page start, end, order + function _get_start_end(&$args, $default = 15) + { + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : $default; + $start = 0; + $end = $pp; - if (empty($zocalo_server) || empty($zocalo_queue)) { - $message = 'Zocalo server or queue not specified.'; - error_log($message); - if (isset($error_code)) { - $this->_error($message, $error_code); - } + if ($this->has_arg('page')) + { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + array_push($args, $start); + array_push($args, $end); + } + + function _get_order($cols, $default) + { + if ($this->has_arg('sort_by')) + { + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + return $cols[$this->arg('sort_by')] . ' ' . $dir; + } + else + return $default; + } + + + /** + * Determine processed dir path based on data collection results + * This function needs to find the samples and investigators for this container + * + * @param array $dc Array of data collection variables, must container VIS, DIR, IMP and RUN + * @param string $location directory nane to append to visit directory that holds processed results + * @return string Returns processed directory path (falls back to legacy dir with extra underscore) + */ + function get_visit_processed_dir($dc, $location) + { + $root = preg_replace('/' . $dc['VIS'] . '/', $dc['VIS'] . $location, $dc['DIR'], 1) . $dc['IMP'] . '_' . $dc['RUN'] . '/'; + if (!is_dir($root)) + { + $root = preg_replace('/' . $dc['VIS'] . '/', $dc['VIS'] . $location, $dc['DIR'], 1) . $dc['IMP'] . '_' . $dc['RUN'] . '_' . '/'; + } + return $root; + } + + + function _submit_zocalo_recipe($recipe, $parameters, $error_code = 500) + { + global $zocalo_mx_reprocess_queue; + + if (isset($zocalo_mx_reprocess_queue)) + { + // Send job to processing queue + $zocalo_message = array( + 'recipes' => array( + $recipe, + ), + 'parameters' => $parameters, + ); + $this->_send_zocalo_message($zocalo_mx_reprocess_queue, $zocalo_message, $error_code); + } + } + + + function _send_zocalo_message($zocalo_queue, $zocalo_message, $error_code = 500) + { + global + $zocalo_server, + $zocalo_username, + $zocalo_password; + + if (empty($zocalo_server) || empty($zocalo_queue)) + { + $message = 'Zocalo server or queue not specified.'; + error_log($message); + if (isset($error_code)) + { + $this->_error($message, $error_code); } + } - try { - error_log("Sending message" . var_export($zocalo_message, true)); - $queue = new Queue($zocalo_server, $zocalo_username, $zocalo_password); - $queue->send($zocalo_queue, $zocalo_message, true, $this->user->login); - } catch (Exception $e) { - $message = $e->getMessage(); - error_log($message); - if (isset($error_code)) { - $this->_error($message, $error_code); - } + try + { + error_log("Sending message" . var_export($zocalo_message, true)); + $queue = new Queue($zocalo_server, $zocalo_username, $zocalo_password); + $queue->send($zocalo_queue, $zocalo_message, true, $this->user->loginId); + } + catch (Exception $e) + { + $message = $e->getMessage(); + error_log($message); + if (isset($error_code)) + { + $this->_error($message, $error_code); } } + } + + protected function haltIfLackingPermission($permission) + { + $this->user->can($permission, $this->app); + } } diff --git a/api/src/Page/Assign.php b/api/src/Page/Assign.php deleted file mode 100644 index 608295afe..000000000 --- a/api/src/Page/Assign.php +++ /dev/null @@ -1,185 +0,0 @@ - '\w+\d+-\d+', 'cid' => '\d+', 'did' => '\d+', 'pos' => '\d+', 'bl' => '[\w-]+'); - - public static $dispatch = array(array('/visits(/:visit)', 'get', '_blsr_visits'), - array('/assign', 'get', '_assign'), - array('/unassign', 'get', '_unassign'), - array('/deact', 'get', '_deactivate'), - array('/names', 'get', '_get_puck_names'), - - ); - - var $def = 'unassign'; - - - # ------------------------------------------------------------------------ - # Assign a container - function _assign() { - if (!$this->has_arg('visit')) $this->_error('No visit specified'); - if (!$this->has_arg('cid')) $this->_error('No container id specified'); - if (!$this->has_arg('pos')) $this->_error('No position specified'); - - $cs = $this->db->pq("SELECT d.dewarid,bl.beamlinename,c.containerid,c.code FROM container c - INNER JOIN dewar d ON d.dewarid = c.dewarid - INNER JOIN shipping s ON s.shippingid = d.shippingid - INNER JOIN blsession bl ON bl.proposalid = s.proposalid - INNER JOIN proposal p ON s.proposalid = p.proposalid - WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), bl.visit_number) LIKE :1 AND c.containerid=:2", array($this->arg('visit'), $this->arg('cid'))); - - if (sizeof($cs) > 0) { - $c = $cs[0]; - $this->db->pq("UPDATE dewar SET dewarstatus='processing' WHERE dewarid=:1", array($c['DEWARID'])); - - $this->db->pq("UPDATE container SET beamlinelocation=:1,samplechangerlocation=:2,containerstatus='processing' WHERE containerid=:3", array($c['BEAMLINENAME'], $this->arg('pos'), $c['CONTAINERID'])); - $this->db->pq("INSERT INTO containerhistory (containerid,status,location,beamlinename) VALUES (:1,:2,:3,:4)", array($c['CONTAINERID'], 'processing', $this->arg('pos'), $c['BEAMLINENAME'])); - $this->_update_history($c['DEWARID'], 'processing', $c['BEAMLINENAME'], $c['CODE'].' => '.$this->arg('pos')); - - $this->_output(1); - } - - $this->_output(0); - } - - # ------------------------------------------------------------------------ - # Unassign a container - function _unassign() { - if (!$this->has_arg('visit')) $this->_error('No visit specified'); - if (!$this->has_arg('cid')) $this->_error('No container id specified'); - - $cs = $this->db->pq("SELECT d.dewarid,bl.beamlinename,c.containerid FROM container c - INNER JOIN dewar d ON d.dewarid = c.dewarid - INNER JOIN shipping s ON s.shippingid = d.shippingid - INNER JOIN blsession bl ON bl.proposalid = s.proposalid - INNER JOIN proposal p ON s.proposalid = p.proposalid - WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), bl.visit_number) LIKE :1 AND c.containerid=:2", array($this->arg('visit'), $this->arg('cid'))); - - if (sizeof($cs) > 0) { - $c = $cs[0]; - - $this->db->pq("UPDATE container SET samplechangerlocation='',beamlinelocation='',containerstatus='at facility' WHERE containerid=:1",array($c['CONTAINERID'])); - $this->db->pq("INSERT INTO containerhistory (containerid,status,beamlinename) VALUES (:1,:2,:3)", array($c['CONTAINERID'], 'at facility', $c['BEAMLINENAME'])); - //$this->_update_history($c['DEWARID'], 'unprocessing'); - - $this->_output(1); - } - $this->_output(0); - } - - - # ------------------------------------------------------------------------ - # Deactivate a dewar - function _deactivate() { - if (!$this->has_arg('visit') && !$this->has_arg('prop')) $this->_error('No visit or proposal specified'); - if (!$this->has_arg('did')) $this->_error('No dewar id specified'); - - if ($this->has_arg('visit')) { - $where = "CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), bl.visit_number) LIKE :1"; - $arg = $this->arg('visit'); - } else { - $where = "p.proposalid=:1"; - $arg = $this->proposalid; - } - - $ds = $this->db->pq("SELECT d.dewarid FROM dewar d - INNER JOIN shipping s ON s.shippingid = d.shippingid - INNER JOIN blsession bl ON bl.proposalid = s.proposalid - INNER JOIN proposal p ON s.proposalid = p.proposalid - WHERE $where AND d.dewarid=:2", array($arg, $this->arg('did'))); - - if (sizeof($ds) > 0) { - $this->_update_history($this->arg('did'), 'unprocessing'); - - $conts = $this->db->pq("SELECT containerid as id FROM container WHERE dewarid=:1", array($this->arg('did'))); - foreach ($conts as $c) { - $this->db->pq("UPDATE container SET containerstatus='at facility', samplechangerlocation='', beamlinelocation='' WHERE containerid=:1", array($c['ID'])); - $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,:2)", array($c['ID'], 'at facility')); - } - $this->_output(1); - - } - $this->_output(0); - } - - - function _update_history($did,$status,$bl=null,$ext=null) { - # Update history - $st = $status; - if ($ext) $st .= ' ('.$ext.')'; - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) - VALUES (s_dewartransporthistory.nextval,:1,:2,:3,CURRENT_TIMESTAMP)", array($did, $st,$bl)); - - # Update dewar status - if ($status == 'unprocessing') $status = 'at facility'; - $this->db->pq("UPDATE dewar set dewarstatus=:2 WHERE dewarid=:1", array($did, $status)); - } - - - # ------------------------------------------------------------------------ - # Return visits for beamline - function _blsr_visits($visit=null) { - $visits = $this->blsr_visits(); - - if ($visit) { - foreach ($visits as $i => $v) { - if ($v['VISIT'] == $visit) { - $this->_output($v); - return; - } - } - $this->_error('No such visit'); - } else $this->_output($visits); - } - - - # ------------------------------------------------------------------------ - # Puck names from puck scanner - # BL03I-MO-ROBOT-01:PUCK_01_NAME - function _get_puck_names() { - global $bl_pv_map; - session_write_close(); - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - if (!array_key_exists($this->arg('bl'), $bl_pv_map)) $this->_error('No such beamline'); - $pv_prefix = $bl_pv_map[$this->arg('bl')]; - - $pvs = array(); - for ($i = 1; $i < 38; $i++) { - $id = $i < 10 ? '0'.$i : $i; - array_push($pvs, $pv_prefix.'-MO-ROBOT-01:PUCK_'.$id.'_NAME'); - } - - $vals = $this->pv(array_values($pvs), true, true); - - $rows = $this->db->pq("SELECT cr.barcode - FROM containerregistry cr - INNER JOIN containerregistry_has_proposal crhp ON crhp.containerregistryid = cr.containerregistryid - WHERE crhp.proposalid = :1", array($this->proposalid)); - - $codes = array(); - foreach ($rows as $r) { - array_push($codes, rtrim($r['BARCODE'])); - } - - $return = array(); - foreach ($vals as $k => $v) { - if (preg_match('/PUCK_(\d+)_NAME/', $k, $mat)) { - if (is_array($v) && sizeof($v)) { - $val = (!in_array($v[0], $codes) && !$this->staff) ? '[Loaded]' : $v[0]; - } else $val = ''; - array_push($return, array('id' => intval($mat[1]), 'name' => $val)); - } - } - - $this->_output($return); - - } -} diff --git a/api/src/Page/Cal.php b/api/src/Page/Cal.php index 7f0c5d96a..428e2433d 100644 --- a/api/src/Page/Cal.php +++ b/api/src/Page/Cal.php @@ -7,7 +7,7 @@ class Cal extends Page { - public static $arg_list = array('mon' => '\w+', 'year' => '\d\d\d\d', 'bl' => '[\w-]+', 'h' => '\w+'); + public static $arg_list = array('mon' => '\w+', 'year' => '\d\d\d\d', 'bl' => '[\w\-]+', 'h' => '\w+'); public static $dispatch = array(array('/ics/h/:h/calendar.ics', 'get', '_export_ics'), array('/ext', 'get', '_external_link'), @@ -55,13 +55,13 @@ function _export_ics() { array_push($args, $hash['CKEY']); } - $visits = $this->db->pq("SELECT s.beamlineoperator as lc, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename as bl, TO_CHAR(s.startdate, 'DD-MM-YYYY') as d, TO_CHAR(s.enddate, 'DD-MM-YYYY') as e, TO_CHAR(s.startdate, 'HH24:MI') as st, TO_CHAR(s.enddate, 'HH24:MI') as en, s.sessionid + $visits = $this->db->pq("SELECT s.beamlineoperator as lc, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename as bl, TO_CHAR(s.startdate, 'DD-MM-YYYY') as d, TO_CHAR(s.enddate, 'DD-MM-YYYY') as e, TO_CHAR(s.startdate, 'HH24:MI') as st, TO_CHAR(s.enddate, 'HH24:MI') as en, s.sessionid FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE s.startdate > TO_DATE(:1,'YYYY') $where ORDER BY s.startdate, s.beamlinename", $args); - $user_tmp = $this->db->pq("SELECT s.sessionid, pe.login, CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as fullname, shp.role + $user_tmp = $this->db->pq("SELECT s.sessionid, pe.login, CONCAT(pe.givenname, ' ', pe.familyname) as fullname, shp.role FROM person pe INNER JOIN session_has_person shp ON shp.personid = pe.personid INNER JOIN blsession s ON s.sessionid = shp.sessionid diff --git a/api/src/Page/Cell.php b/api/src/Page/Cell.php index f639a908a..80fbcc071 100644 --- a/api/src/Page/Cell.php +++ b/api/src/Page/Cell.php @@ -8,101 +8,109 @@ class Cell extends Page { - public static $arg_list = array('page' => '\d+', - 'a' => '\d+(.\d+)?', - 'b' => '\d+(.\d+)?', - 'c' => '\d+(.\d+)?', - 'al' => '\d+(.\d+)?', - 'be' => '\d+(.\d+)?', - 'ga' => '\d+(.\d+)?', - 'tol' => '\d+(.\d+)?', - 'res' => '\d+(.\d+)?', - 'sg' => '(\w|\s)+', - 'id' => '\d+', - 'pdb' => '\w+', - 'title' => '.*', - 'author' => '.*', - 'bl' => '.*', - 'year' => '\d\d\d\d-\d\d-\d\d', - 's' => '[\w|\s|-|,]+', - 't' => '\w+', - 'pp' => '\d+', - 'page' => '\d+', - 'dist' => '\d+(.\d+)?', - 'pdb' => '\w+', - 'download' => '\d', - ); - - - public static $dispatch = array(array('', 'get', '_cells'), - array('/analysed', 'get', '_analysed'), - array('/process', 'post', '_process_pdbs'), - array('/pdbs', 'get', '_get_processed'), - - array('/ap', 'get', '_autoproc'), - array('/state', 'get', '_state'), - array('/bl', 'get', '_beamlines'), - - array('/pdb', 'get', '_proxy_pdb'), - array('/rcsb', 'get', '_proxy_pdbs'), - ); - - - # ------------------------------------------------------------------------ - # List data collections / visits for a particular cell - function _cells($output=true) { - global $ap_types; - session_write_close(); - - $tol = $this->has_arg('tol') ? $this->arg('tol') : 0.01; - - $args = array(); - foreach (array('a', 'b', 'c', 'al', 'be', 'ga') as $p) { - if (!$this->has_arg($p)) $this->_error('One or more unit cell parameters are missing'); - - array_push($args, $this->arg($p)*(1-$tol)); - array_push($args, $this->arg($p)*(1+$tol)); - } - - $tot_args = $args; - foreach (array('a', 'b', 'c', 'al', 'be', 'ga') as $p) array_push($args, $this->arg($p)); - - if ($this->has_arg('year')) { - array_push($args, '00:01 '.$this->arg('year')); - } else { - array_push($args, strftime('%H:%M %Y-%m-%d')); - } - array_push($tot_args, $args[sizeof($args)-1]); - - $rest = ''; - $sgt = ''; - - if ($this->has_arg('res')) { - $res = 'AND apss.resolutionlimithigh <= :'.(sizeof($args)+1); - array_push($args, $this->arg('res')); - $rest = 'AND apss.resolutionlimithigh <= :'.(sizeof($tot_args)+1); - array_push($tot_args, $this->arg('res')); - } else $res = ''; - - if ($this->has_arg('sg')) { - $sg = "AND REPLACE(ap.spacegroup, ' ', '') LIKE :".(sizeof($args)+1); - array_push($args, $this->arg('sg')); - $sgt = "AND REPLACE(ap.spacegroup, ' ', '') LIKE :".(sizeof($tot_args)+1); - //$sgt = 'AND ap.spacegroup LIKE :'.(sizeof($tot_args)+1); - array_push($tot_args, $this->arg('sg')); - } else $sg = ''; - - $nostafft = ''; - if (!$this->staff) { - $nostaff = "INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid AND shp.personid=:".(sizeof($args)+1); - array_push($args, $this->user->personid); - - $nostafft = "INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid AND shp.personid=:".(sizeof($tot_args)+1); - array_push($tot_args, $this->user->personid); - } else $nostaff = ''; - - - $tot = $this->db->pq("SELECT count(ap.refinedcell_a) as tot + public static $arg_list = array('a' => '\d+(.\d+)?', + 'b' => '\d+(.\d+)?', + 'c' => '\d+(.\d+)?', + 'al' => '\d+(.\d+)?', + 'be' => '\d+(.\d+)?', + 'ga' => '\d+(.\d+)?', + 'tol' => '\d+(.\d+)?', + 'res' => '\d+(.\d+)?', + 'sg' => '(\w|\s)+', + 'id' => '\d+', + 'title' => '.*', + 'author' => '.*', + 'bl' => '.*', + 'year' => '\d\d\d\d-\d\d-\d\d', + 's' => '[\w|\s|\-|,]+', + 't' => '\w+', + 'pp' => '\d+', + 'page' => '\d+', + 'dist' => '\d+(.\d+)?', + 'pdb' => '\w+', + 'download' => '\d', + ); + + + public static $dispatch = array(array('', 'get', '_cells'), + array('/analysed', 'get', '_analysed'), + array('/process', 'post', '_process_pdbs'), + array('/pdbs', 'get', '_get_processed'), + + array('/ap', 'get', '_autoproc'), + array('/state', 'get', '_state'), + array('/bl', 'get', '_beamlines'), + + array('/pdb', 'get', '_proxy_pdb'), + array('/rcsb', 'get', '_proxy_pdbs'), + ); + + + # ------------------------------------------------------------------------ + # List data collections / visits for a particular cell + function _cells($output = true) + { + global $ap_types; + session_write_close(); + + $tol = $this->has_arg('tol') ? $this->arg('tol') : 0.01; + + $args = array(); + foreach (array('a', 'b', 'c', 'al', 'be', 'ga') as $p) { + if (!$this->has_arg($p)) + $this->_error('One or more unit cell parameters are missing'); + + array_push($args, $this->arg($p) * (1 - $tol)); + array_push($args, $this->arg($p) * (1 + $tol)); + } + + $tot_args = $args; + foreach (array('a', 'b', 'c', 'al', 'be', 'ga') as $p) + array_push($args, $this->arg($p)); + + if ($this->has_arg('year')) { + array_push($args, '00:01 ' . $this->arg('year')); + } + else { + array_push($args, strftime('%H:%M %Y-%m-%d')); + } + array_push($tot_args, $args[sizeof($args) - 1]); + + $rest = ''; + $sgt = ''; + + if ($this->has_arg('res')) { + $res = 'AND apss.resolutionlimithigh <= :' . (sizeof($args) + 1); + array_push($args, $this->arg('res')); + $rest = 'AND apss.resolutionlimithigh <= :' . (sizeof($tot_args) + 1); + array_push($tot_args, $this->arg('res')); + } + else + $res = ''; + + if ($this->has_arg('sg')) { + $sg = "AND REPLACE(ap.spacegroup, ' ', '') LIKE :" . (sizeof($args) + 1); + array_push($args, $this->arg('sg')); + $sgt = "AND REPLACE(ap.spacegroup, ' ', '') LIKE :" . (sizeof($tot_args) + 1); + //$sgt = 'AND ap.spacegroup LIKE :'.(sizeof($tot_args)+1); + array_push($tot_args, $this->arg('sg')); + } + else + $sg = ''; + + $nostafft = ''; + if (!$this->staff) { + $nostaff = "INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid AND shp.personid=:" . (sizeof($args) + 1); + array_push($args, $this->user->personId); + + $nostafft = "INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid AND shp.personid=:" . (sizeof($tot_args) + 1); + array_push($tot_args, $this->user->personId); + } + else + $nostaff = ''; + + + $tot = $this->db->pq("SELECT count(ap.refinedcell_a) as tot FROM autoprocintegration api INNER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid INNER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid @@ -114,29 +122,32 @@ function _cells($output=true) { INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON s.proposalid = p.proposalid $nostafft WHERE p.proposalcode != 'in' AND apss.scalingstatisticstype LIKE 'overall' AND (ap.refinedcell_a BETWEEN :1 AND :2) AND (ap.refinedcell_b BETWEEN :3 AND :4) AND (ap.refinedcell_c BETWEEN :5 AND :6) AND (ap.refinedcell_alpha BETWEEN :7 AND :8) AND (ap.refinedcell_beta BETWEEN :9 AND :10) AND (ap.refinedcell_gamma BETWEEN :11 AND :12) AND TO_DATE(:13, 'HH24:MI YYYY-MM-DD') >= dc.starttime $rest $sgt", $tot_args); - - if (sizeof($tot)) $tot = $tot[0]['TOT']; - else $tot = 0; - - $start = 0; - $pp = $this->has_arg('pp') ? $this->arg('pp') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $pgs = intval($tot/$pp); - if ($tot % $pp != 0) $pgs++; - - $rows = $this->db->paginate("SELECT s.sessionid, api.autoprocprogramid, api.autoprocintegrationid, sqrt(power(ap.refinedcell_a-:13,2)+power(ap.refinedcell_b-:14,2)+power(ap.refinedcell_c-:15,2)+power(ap.refinedcell_alpha-:16,2)+power(ap.refinedcell_beta-:17,2)+power(ap.refinedcell_gamma-:18,2)) as dist, s.beamlinename as bl, app.processingcommandline as type, apss.ntotalobservations as ntobs, apss.ntotaluniqueobservations as nuobs, apss.resolutionlimitlow as rlow, apss.resolutionlimithigh as rhigh, apss.scalingstatisticstype as shell, apss.rmerge, apss.completeness, apss.multiplicity, apss.meanioversigi as isigi, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, ap.refinedcell_c as cell_c, ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga, dc.datacollectionid as id, TO_CHAR(dc.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, dc.imagedirectory as dir, dc.filetemplate, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, dc.numberofimages as numimg, dc.axisrange, dc.axisstart, dc.wavelength, dc.transmission, dc.exposuretime, IFNULL(dc.rotationaxis, 'Omega') as rotationaxis + + if (sizeof($tot)) + $tot = $tot[0]['TOT']; + else + $tot = 0; + + $start = 0; + $pp = $this->has_arg('pp') ? $this->arg('pp') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $pgs = intval($tot / $pp); + if ($tot % $pp != 0) + $pgs++; + + $rows = $this->db->paginate("SELECT s.sessionid, api.autoprocprogramid, api.autoprocintegrationid, sqrt(power(ap.refinedcell_a-:13,2)+power(ap.refinedcell_b-:14,2)+power(ap.refinedcell_c-:15,2)+power(ap.refinedcell_alpha-:16,2)+power(ap.refinedcell_beta-:17,2)+power(ap.refinedcell_gamma-:18,2)) as dist, s.beamlinename as bl, app.processingcommandline as type, apss.ntotalobservations as ntobs, apss.ntotaluniqueobservations as nuobs, apss.resolutionlimitlow as rlow, apss.resolutionlimithigh as rhigh, apss.scalingstatisticstype as shell, apss.rmerge, apss.completeness, apss.multiplicity, apss.meanioversigi as isigi, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, ap.refinedcell_c as cell_c, ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga, dc.datacollectionid as id, TO_CHAR(dc.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, dc.imagedirectory as dir, dc.filetemplate, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, dc.numberofimages as numimg, dc.axisrange, dc.axisstart, dc.wavelength, dc.transmission, dc.exposuretime, IFNULL(dc.rotationaxis, 'Omega') as rotationaxis FROM autoprocintegration api INNER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid INNER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid @@ -149,80 +160,87 @@ function _cells($output=true) { INNER JOIN proposal p ON s.proposalid = p.proposalid $nostaff WHERE p.proposalcode != 'in' AND apss.scalingstatisticstype LIKE 'overall' AND (ap.refinedcell_a BETWEEN :1 AND :2) AND (ap.refinedcell_b BETWEEN :3 AND :4) AND (ap.refinedcell_c BETWEEN :5 AND :6) AND (ap.refinedcell_alpha BETWEEN :7 AND :8) AND (ap.refinedcell_beta BETWEEN :9 AND :10) AND (ap.refinedcell_gamma BETWEEN :11 AND :12) AND TO_DATE(:19, 'HH24:MI YYYY-MM-DD') >= dc.starttime $res $sg ORDER BY dist", $args); - - foreach ($rows as &$dc) { - foreach ($ap_types as $id => $name) { - if (strpos($dc['TYPE'], $id) !== false) { - $dc['TYPE'] = $name; - break; - } + + foreach ($rows as &$dc) { + foreach ($ap_types as $id => $name) { + if (strpos($dc['TYPE'], $id) !== false) { + $dc['TYPE'] = $name; + break; } - + } + - $users = $this->db->pq("SELECT p.title, p.familyname, p.givenname FROM person p + $users = $this->db->pq("SELECT p.title, p.familyname, p.givenname FROM person p INNER JOIN session_has_person shp ON p.personid = shp.personid WHERE shp.sessionid=:1", array($dc['SESSIONID'])); - $dc['USERS'] = array(); - foreach ($users as $u) { - array_push($dc['USERS'], $u['TITLE'].' '.$u['GIVENNAME'].' '.$u['FAMILYNAME']); - } - - $dc['DIR'] = $this->ads($dc['DIR']); - $dc['DIR'] = substr($dc['DIR'], strpos($dc['DIR'], $dc['VISIT'])+strlen($dc['VISIT'])+1); - - foreach(array('TRANSMISSION', 'WAVELENGTH', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_AL', 'CELL_BE', 'CELL_GA', 'EXPOSURETIME', 'AXISRANGE', 'RLOW', 'RHIGH', 'COMPLETENESS', 'MULTIPLICITY', 'RMERGE', 'ISIGI') as $k) { - $dc[$k] = number_format($dc[$k], 3); - } + $dc['USERS'] = array(); + foreach ($users as $u) { + array_push($dc['USERS'], $u['TITLE'] . ' ' . $u['GIVENNAME'] . ' ' . $u['FAMILYNAME']); + } + + $dc['DIR'] = $this->ads($dc['DIR']); + $dc['DIR'] = substr($dc['DIR'], strpos($dc['DIR'], $dc['VISIT']) + strlen($dc['VISIT']) + 1); + + foreach (array('TRANSMISSION', 'WAVELENGTH', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_AL', 'CELL_BE', 'CELL_GA', 'EXPOSURETIME', 'AXISRANGE', 'RLOW', 'RHIGH', 'COMPLETENESS', 'MULTIPLICITY', 'RMERGE', 'ISIGI') as $k) { + $dc[$k] = number_format($dc[$k], 3); } - - if ($output) $this->_output(array($tot, $pgs, $rows)); - else return array($tot, $rows); } - - function _write_csv($data, $filename) { - header('Content-Type:application/csv'); - header("Content-Disposition:attachment;filename=$filename.csv"); + if ($output) + $this->_output(array($tot, $pgs, $rows)); + else + return array($tot, $rows); + } - print implode(',',array_keys($data[0]))."\n"; - foreach ($data as $d) { - print implode(',', array_values($d))."\n"; - } + function _write_csv($data, $filename) + { + header('Content-Type:application/csv'); + header("Content-Disposition:attachment;filename=$filename.csv"); + + print implode(',', array_keys($data[0])) . "\n"; + + foreach ($data as $d) { + print implode(',', array_values($d)) . "\n"; } + } - # ------------------------------------------------------------------------ - # Autoprocessing stats - function _autoproc() { - if (!$this->staff) $this->_error('Access Denied', 403); + # ------------------------------------------------------------------------ + # Autoprocessing stats + function _autoproc() + { + if (!$this->staff) + $this->_error('Access Denied', 403); - $stats = $this->db->pq("SELECT TO_CHAR(pdbdate, 'YYYY') as year, count(pdbentryid) as tot, count(case when (beamlinematch=1 OR (autoproccount > 0 and authormatch=1)) and distance < :1 then 1 end) as ap + $stats = $this->db->pq("SELECT TO_CHAR(pdbdate, 'YYYY') as year, count(pdbentryid) as tot, count(case when (beamlinematch=1 OR (autoproccount > 0 and authormatch=1)) and distance < :1 then 1 end) as ap FROM pdbentry WHERE pdbdate > TO_DATE('2010-05', 'YYYY-MM') GROUP BY TO_CHAR(pdbdate, 'YYYY') ORDER BY TO_CHAR(pdbdate, 'YYYY')", array($this->has_arg('dist') ? $this->arg('dist') : 0.2)); - $tot = array('AP' => 0, 'MAN' => 0); - foreach ($stats as $i => &$s) { - $s['TOT'] = intval($s['TOT']); - $s['AP'] = intval($s['AP']); - $s['MAN'] = $s['TOT'] - $s['AP']; + $tot = array('AP' => 0, 'MAN' => 0); + foreach ($stats as $i => &$s) { + $s['TOT'] = intval($s['TOT']); + $s['AP'] = intval($s['AP']); + $s['MAN'] = $s['TOT'] - $s['AP']; - $tot['MAN'] += $s['MAN']; - $tot['AP'] += $s['AP']; - } - - $this->has_arg('download') ? $this->_write_csv($stats, 'pdb_apvman') : $this->_output(array($tot, $stats)); + $tot['MAN'] += $s['MAN']; + $tot['AP'] += $s['AP']; } + $this->has_arg('download') ? $this->_write_csv($stats, 'pdb_apvman') : $this->_output(array($tot, $stats)); + } + - # ------------------------------------------------------------------------ - # State stats - function _state() { - if (!$this->staff) $this->_error('Access Denied', 403); + # ------------------------------------------------------------------------ + # State stats + function _state() + { + if (!$this->staff) + $this->_error('Access Denied', 403); - $stats = $this->db->pq("SELECT + $stats = $this->db->pq("SELECT count(case when (beamlinematch=1) then 1 end) as matched, count(case when (autoproccount>0 and beamlinematch != 1 and authormatch!=1) then 1 end) as nomatch, count(case when (autoproccount=0) then 1 end) as noresults, @@ -230,33 +248,36 @@ function _state() { FROM pdbentry WHERE pdbdate > TO_DATE('2010-05', 'YYYY-MM')"); - $stats = $stats[0]; - foreach ($stats as $i => &$v) { - $v = intval($v); - } - - $this->_output($stats); + $stats = $stats[0]; + foreach ($stats as $i => &$v) { + $v = intval($v); } + $this->_output($stats); + } - # ------------------------------------------------------------------------ - # Beamline stats - function _beamlines() { - if (!$this->staff) $this->_error('Access Denied', 403); - global $facility_pdb_ident; + # ------------------------------------------------------------------------ + # Beamline stats + function _beamlines() + { + if (!$this->staff) + $this->_error('Access Denied', 403); - $replace = 'p.pdbbeamlinename'; - foreach ($facility_pdb_ident as $i) $replace = "REPLACE($replace, '$i ', '')"; + global $facility_pdb_ident; - $pdb = $this->db->pq("SELECT TO_CHAR(p.pdbdate, 'YYYY') as year, $replace as bl, count(p.pdbentryid) as count + $replace = 'p.pdbbeamlinename'; + foreach ($facility_pdb_ident as $i) + $replace = "REPLACE($replace, '$i ', '')"; + + $pdb = $this->db->pq("SELECT TO_CHAR(p.pdbdate, 'YYYY') as year, $replace as bl, count(p.pdbentryid) as count FROM pdbentry p WHERE p.pdbdate > TO_DATE('2010-05', 'YYYY-MM') GROUP BY $replace, TO_CHAR(p.pdbdate, 'YYYY') ORDER BY TO_CHAR(p.pdbdate, 'YYYY')"); - $isp = $this->db->pq("SELECT TO_CHAR(p.pdbdate, 'YYYY') as year, CASE WHEN p.autoprocprogramid > 0 THEN UPPER(s.beamlinename) ELSE $replace END as bl, count(p.pdbentryid) as count + $isp = $this->db->pq("SELECT TO_CHAR(p.pdbdate, 'YYYY') as year, CASE WHEN p.autoprocprogramid > 0 THEN UPPER(s.beamlinename) ELSE $replace END as bl, count(p.pdbentryid) as count FROM pdbentry p LEFT OUTER JOIN autoprocintegration api ON api.autoprocprogramid = p.autoprocprogramid LEFT OUTER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid @@ -266,72 +287,75 @@ function _beamlines() { GROUP BY CASE WHEN p.autoprocprogramid > 0 THEN UPPER(s.beamlinename) ELSE $replace END, TO_CHAR(p.pdbdate, 'YYYY') ORDER BY TO_CHAR(p.pdbdate, 'YYYY')"); - foreach ($pdb as $i => &$s) { - $s['COUNT'] = intval(($s['COUNT'])); - } - - foreach ($isp as $i => &$s) { - $s['COUNT'] = intval(($s['COUNT'])); - } - - $this->_output(array($pdb, $isp)); + foreach ($pdb as $i => &$s) { + $s['COUNT'] = intval(($s['COUNT'])); } - - # ------------------------------------------------------------------------ - # List pdb codes that have been analysed - function _analysed() { - if (!$this->staff) $this->_error('Access Denied', 403); + foreach ($isp as $i => &$s) { + $s['COUNT'] = intval(($s['COUNT'])); + } - $args = array(); - $where = ''; + $this->_output(array($pdb, $isp)); + } - if ($this->has_arg('t')) { - if ($this->arg('t') == 'match' ) { - $where .= ' AND p.beamlinematch=1'; - } - if ($this->arg('t') == 'mismatch' ) { - $where .= ' AND p.autoproccount > 0 AND p.authormatch=1 AND p.beamlinematch!=1'; - } + # ------------------------------------------------------------------------ + # List pdb codes that have been analysed + function _analysed() + { + if (!$this->staff) + $this->_error('Access Denied', 403); - if ($this->arg('t') == 'noresults' ) { - $where .= ' AND p.autoproccount < 1'; - } + $args = array(); + $where = ''; - if ($this->arg('t') == 'nomatch' ) { - $where .= ' AND p.autoproccount > 0 and p.authormatch=0 and p.beamlinematch=0'; - } + if ($this->has_arg('t')) { + if ($this->arg('t') == 'match') { + $where .= ' AND p.beamlinematch=1'; } + if ($this->arg('t') == 'mismatch') { + $where .= ' AND p.autoproccount > 0 AND p.authormatch=1 AND p.beamlinematch!=1'; + } - if ($this->has_arg('s')) { - $s = str_replace('_', '$_', $this->arg('s')); - - $st = sizeof($args) + 1; - $where .= " AND (lower(p.code) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(p.pdbauthors) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) ESCAPE '$' OR lower(p.pdbbeamlinename) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')) ESCAPE '$')"; - - for ($i = 0; $i < 3; $i++) array_push($args, $s); + if ($this->arg('t') == 'noresults') { + $where .= ' AND p.autoproccount < 1'; } - $tot = $this->db->pq("SELECT count(p.pdbentryid) as tot FROM pdbentry p WHERE 1=1 $where", $args); - $tot = intval($tot[0]['TOT']); - - $start = 0; - $pp = $this->has_arg('pp') ? $this->arg('pp') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; + if ($this->arg('t') == 'nomatch') { + $where .= ' AND p.autoproccount > 0 and p.authormatch=0 and p.beamlinematch=0'; } + } + + + if ($this->has_arg('s')) { + $s = str_replace('_', '$_', $this->arg('s')); $st = sizeof($args) + 1; - array_push($args, $start); - array_push($args, $end); + $where .= " AND (lower(p.code) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(p.pdbauthors) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) ESCAPE '$' OR lower(p.pdbbeamlinename) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')) ESCAPE '$')"; + + for ($i = 0; $i < 3; $i++) + array_push($args, $s); + } + + $tot = $this->db->pq("SELECT count(p.pdbentryid) as tot FROM pdbentry p WHERE 1=1 $where", $args); + $tot = intval($tot[0]['TOT']); + + $start = 0; + $pp = $this->has_arg('pp') ? $this->arg('pp') : 15; + $end = $pp; - $rows = $this->db->paginate("SELECT p.pdbentryid, p.autoprocprogramid, p.code, p.resolution, p.pdbbeamlinename, p.beamlines, p.distance, p.autoproccount, p.beamlinematch, p.authormatch, dc.datacollectionid, s.beamlinename, TO_CHAR(p.pdbdate, 'YYYY-MM-DD') as pdbdate + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT p.pdbentryid, p.autoprocprogramid, p.code, p.resolution, p.pdbbeamlinename, p.beamlines, p.distance, p.autoproccount, p.beamlinematch, p.authormatch, dc.datacollectionid, s.beamlinename, TO_CHAR(p.pdbdate, 'YYYY-MM-DD') as pdbdate FROM pdbentry p LEFT OUTER JOIN autoprocintegration api ON api.autoprocprogramid = p.autoprocprogramid LEFT OUTER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid @@ -340,101 +364,110 @@ function _analysed() { WHERE 1=1 $where ORDER BY p.pdbdate DESC", $args); - foreach ($rows as $i => &$r) { - $r['BEAMLINEMATCH'] = intval($r['BEAMLINEMATCH']); - $r['AUTHORMATCH'] = intval($r['AUTHORMATCH']); - } - - $this->_output(array($tot, $rows)); + foreach ($rows as $i => &$r) { + $r['BEAMLINEMATCH'] = intval($r['BEAMLINEMATCH']); + $r['AUTHORMATCH'] = intval($r['AUTHORMATCH']); } + $this->_output(array($tot, $rows)); + } + + + # ------------------------------------------------------------------------ + # Process a pdb from RCSB + function _process_pdbs() + { + if (!$this->staff) + $this->_error('Access Denied', 403); + + global $facility_pdb_ident; + if (!$this->has_arg('bl')) + $this->_error('No beamline specified'); + + list($tot, $rows) = $this->_cells(false); + + $blmatch = 0; + $umatch = 0; + $bls = array(); + foreach ($rows as $r) { + $bls[$r['BL']] = 1; - # ------------------------------------------------------------------------ - # Process a pdb from RCSB - function _process_pdbs() { - if (!$this->staff) $this->_error('Access Denied', 403); - - global $facility_pdb_ident; - if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - - list($tot, $rows) = $this->_cells(false); - - $blmatch = 0; - $umatch = 0; - $bls = array(); - foreach ($rows as $r) { - $bls[$r['BL']] = 1; - - $bl_to_match = $this->arg('bl'); - foreach ($facility_pdb_ident as $i) $bl_to_match = str_replace($i.' ', '', $bl_to_match); - if ($bl_to_match == strtoupper($r['BL'])) $blmatch = 1; - - foreach ($r['USERS'] as $u) { - $parts = explode(' ', $u); - - if (stripos($this->arg('author'), end($parts)) !== false) { - $umatch = 1; - } + $bl_to_match = $this->arg('bl'); + foreach ($facility_pdb_ident as $i) + $bl_to_match = str_replace($i . ' ', '', $bl_to_match); + if ($bl_to_match == strtoupper($r['BL'])) + $blmatch = 1; + + foreach ($r['USERS'] as $u) { + $parts = explode(' ', $u); + + if (stripos($this->arg('author'), end($parts)) !== false) { + $umatch = 1; } } + } - $bls = implode(', ', array_keys($bls)); - $aid = sizeof($rows) ? $rows[0]['AUTOPROCPROGRAMID'] : null; - $dist = sizeof($rows) ? $rows[0]['DIST'] : null; + $bls = implode(', ', array_keys($bls)); + $aid = sizeof($rows) ? $rows[0]['AUTOPROCPROGRAMID'] : null; + $dist = sizeof($rows) ? $rows[0]['DIST'] : null; - $title = $this->has_arg('title') ? $this->arg('title') : ''; - $author = $this->has_arg('author') ? $this->arg('author') : ''; + $title = $this->has_arg('title') ? $this->arg('title') : ''; + $author = $this->has_arg('author') ? $this->arg('author') : ''; - $this->db->pq("INSERT INTO pdbentry (pdbentryid, autoprocprogramid, code, cell_a, cell_b, cell_c, cell_alpha, cell_beta, cell_gamma, resolution, pdbtitle, pdbauthors, pdbdate, pdbbeamlinename, beamlines, distance, autoproccount, beamlinematch, authormatch) - VALUES (s_pdbentry.nextval, :1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, TO_DATE(:12, 'YYYY-MM-DD'), :13, :14, :15, :16, :17, :18)", - array($aid, $this->arg('pdb'), $this->arg('a'), $this->arg('b'), $this->arg('c'), $this->arg('al'), $this->arg('be'), $this->arg('ga'), $this->arg('res')/1.25, $title, $author, $this->arg('year'), $this->arg('bl'), $bls, $dist, $tot, $blmatch, $umatch)); - - $this->_output(1); - } + $this->db->pq("INSERT INTO pdbentry (pdbentryid, autoprocprogramid, code, cell_a, cell_b, cell_c, cell_alpha, cell_beta, cell_gamma, resolution, pdbtitle, pdbauthors, pdbdate, pdbbeamlinename, beamlines, distance, autoproccount, beamlinematch, authormatch) + VALUES (s_pdbentry.nextval, :1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, TO_DATE(:12, 'YYYY-MM-DD'), :13, :14, :15, :16, :17, :18)", + array($aid, $this->arg('pdb'), $this->arg('a'), $this->arg('b'), $this->arg('c'), $this->arg('al'), $this->arg('be'), $this->arg('ga'), $this->arg('res') / 1.25, $title, $author, $this->arg('year'), $this->arg('bl'), $bls, $dist, $tot, $blmatch, $umatch)); + $this->_output(1); + } - function _get_processed() { - $rows = $this->db->pq("SELECT code from pdbentry"); - $this->_output($rows); - } + function _get_processed() + { + $rows = $this->db->pq("SELECT code from pdbentry"); + $this->_output($rows); + } - function _proxy_pdb() { - if (!$this->has_arg('pdb')) $this->_error('No pdb file specified'); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'http://www.rcsb.org/pdb/rest/customReport?pdbids='.$this->arg('pdb').'&customReportColumns=structureId,structureTitle,unitCellAngleAlpha,unitCellAngleBeta,unitCellAngleGamma,lengthOfUnitCellLatticeA,lengthOfUnitCellLatticeB,lengthOfUnitCellLatticeC,structureAuthor,citationAuthor,firstPage,lastPage,journalName,title,volumeId,publicationYear,diffractionSource,resolution,spaceGroup,releaseDate'); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $response = curl_exec($ch); - curl_close($ch); + function _proxy_pdb() + { + if (!$this->has_arg('pdb')) + $this->_error('No pdb file specified'); - $this->app->contentType('text/xml'); - $this->app->response()->body($response); - } + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'http://www.rcsb.org/pdb/rest/customReport?pdbids=' . $this->arg('pdb') . '&customReportColumns=structureId,structureTitle,unitCellAngleAlpha,unitCellAngleBeta,unitCellAngleGamma,lengthOfUnitCellLatticeA,lengthOfUnitCellLatticeB,lengthOfUnitCellLatticeC,structureAuthor,citationAuthor,firstPage,lastPage,journalName,title,volumeId,publicationYear,diffractionSource,resolution,spaceGroup,releaseDate'); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + + $this->app->contentType('text/xml'); + $this->app->response()->body($response); + } - function _proxy_pdbs() { - global $facility_pdb_site; - $facility_pdb_site_lc = strtolower($facility_pdb_site); + function _proxy_pdbs() + { + global $facility_pdb_site; + $facility_pdb_site_lc = strtolower($facility_pdb_site); - $xml = " + $xml = " org.pdb.query.simple.XrayDiffrnSourceQuery $facility_pdb_site_lc contains $facility_pdb_site "; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, 'http://www.rcsb.org/pdb/rest/search'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $response = curl_exec($ch); - curl_close($ch); - - $this->app->contentType('text/plain'); - $this->app->response()->body($response); - } + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'http://www.rcsb.org/pdb/rest/search'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + + $this->app->contentType('text/plain'); + $this->app->response()->body($response); + } } diff --git a/api/src/Page/Contact.php b/api/src/Page/Contact.php index da324cf54..64d9e7ef9 100644 --- a/api/src/Page/Contact.php +++ b/api/src/Page/Contact.php @@ -7,19 +7,19 @@ class Contact extends Page { - public static $arg_list = array('CARDNAME' => '([\w\s-])+', - 'FAMILYNAME' => '([\w-])+', - 'GIVENNAME' => '([\w-])+', + public static $arg_list = array('CARDNAME' => '([\w\s\-])+', + 'FAMILYNAME' => '([\w\-])+', + 'GIVENNAME' => '([\w\-])+', 'PHONENUMBER' => '.*', 'EMAILADDRESS' => '.*', - 'LABNAME' => '([\w\s-])+', - 'ADDRESS' => '([\w\s-\n,])+', - 'COUNTRY' => '([\w\s-,\(\)\'])+', - 'CITY' => '([\w\s-])+', - 'POSTCODE' => '([\w\s-])+', - 'DEFAULTCOURRIERCOMPANY' => '([\w\s-])+', - 'COURIERACCOUNT' => '([\w-])+', - 'BILLINGREFERENCE' => '([\w\s-])+', + 'LABNAME' => '([\w\s\-])+', + 'ADDRESS' => '([\w\s\-\n,])+', + 'COUNTRY' => '([\w\s\-,\(\)\'])+', + 'CITY' => '([\w\s\-])+', + 'POSTCODE' => '([\w\s\-])+', + 'DEFAULTCOURRIERCOMPANY' => '([\w\s\-])+', + 'COURIERACCOUNT' => '([\w\-])+', + 'BILLINGREFERENCE' => '([\w\s\-])+', 'DEWARAVGTRANSPORTVALUE' => '\d+', 'DEWARAVGCUSTOMSVALUE' => '\d+', @@ -143,20 +143,68 @@ function _add_contact() { } if (!$valid) $this->_error('Missing Fields'); + + $cont = $this->db->pq("SELECT c.labcontactid + FROM labcontact c + WHERE c.cardname=:1 and c.proposalid=:2", array($this->arg('CARDNAME'), $this->proposalid)); + if (sizeof($cont)) $this->_error('The specified card name already exists'); - $this->db->pq("INSERT INTO laboratory (laboratoryid,name,address,city,postcode,country) - VALUES (s_laboratory.nextval, :1, :2, :3, :4, :5) RETURNING laboratoryid INTO :id", - array($this->arg('LABNAME'), $this->arg('ADDRESS'), $this->arg('CITY'), $this->arg('POSTCODE'), $this->arg('COUNTRY'))); - $lid = $this->db->id(); - - $email = $this->has_arg('EMAILADDRESS') ? $this->arg('EMAILADDRESS') : ''; - $phone = $this->has_arg('PHONENUMBER') ? $this->arg('PHONENUMBER') : ''; - - $this->db->pq("INSERT INTO person (personid, givenname, familyname, emailaddress, phonenumber, laboratoryid) - VALUES (s_person.nextval, :1, :2, :3, :4, :5) RETURNING personid INTO :id", - array($this->arg('GIVENNAME'), $this->arg('FAMILYNAME'), $email, $phone, $lid)); + if ($this->has_arg('PERSONID')) { + $pid = $this->arg('PERSONID'); + + $check = $this->db->pq("SELECT c.labcontactid + FROM labcontact c + WHERE c.personid=:1 and c.proposalid=:2", array($pid, $this->proposalid)); + if (sizeof($check)) $this->_error('The specified login already has a lab contact for this proposal'); + + $lab = $this->db->pq("SELECT l.laboratoryid FROM laboratory l + INNER JOIN person p ON l.laboratoryid = p.laboratoryid + WHERE p.personid=:1", array($pid)); + + if (sizeof($lab)) { + # Update laboratory + $lfields = array('LABNAME', 'ADDRESS', 'CITY', 'COUNTRY', 'POSTCODE'); + foreach ($lfields as $i => $f) { + if ($this->has_arg($f)) { + $c = $f == 'LABNAME' ? 'NAME' : $f; + $this->db->pq('UPDATE laboratory SET '.$c.'=:1 WHERE laboratoryid=:2', array($this->arg($f), $lab[0]['LABORATORYID'])); + } + } + } + else { + # Create laboratory + $this->db->pq("INSERT INTO laboratory (laboratoryid,name,address,city,postcode,country) + VALUES (s_laboratory.nextval, :1, :2, :3, :4, :5) RETURNING laboratoryid INTO :id", + array($this->arg('LABNAME'), $this->arg('ADDRESS'), $this->arg('CITY'), $this->arg('POSTCODE'), $this->arg('COUNTRY'))); + $lid = $this->db->id(); + $this->db->pq('UPDATE person SET laboratoryid=:1 WHERE personid=:2', array($lid, $pid)); + } + + # Update person + $pfields = array('FAMILYNAME', 'GIVENNAME', 'PHONENUMBER', 'EMAILADDRESS'); + foreach ($pfields as $i => $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE person SET '.$f.'=:1 WHERE personid=:2', array($this->arg($f), $pid)); + } + } + + } + else { - $pid = $this->db->id(); + $this->db->pq("INSERT INTO laboratory (laboratoryid,name,address,city,postcode,country) + VALUES (s_laboratory.nextval, :1, :2, :3, :4, :5) RETURNING laboratoryid INTO :id", + array($this->arg('LABNAME'), $this->arg('ADDRESS'), $this->arg('CITY'), $this->arg('POSTCODE'), $this->arg('COUNTRY'))); + $lid = $this->db->id(); + + $email = $this->has_arg('EMAILADDRESS') ? $this->arg('EMAILADDRESS') : ''; + $phone = $this->has_arg('PHONENUMBER') ? $this->arg('PHONENUMBER') : ''; + + $this->db->pq("INSERT INTO person (personid, givenname, familyname, emailaddress, phonenumber, laboratoryid) + VALUES (s_person.nextval, :1, :2, :3, :4, :5) RETURNING personid INTO :id", + array($this->arg('GIVENNAME'), $this->arg('FAMILYNAME'), $email, $phone, $lid)); + + $pid = $this->db->id(); + } $c = $this->def_arg('DEFAULTCOURRIERCOMPANY', ''); $ca = $this->has_arg('COURIERACCOUNT') ? $this->arg('COURIERACCOUNT') : ''; diff --git a/api/src/Page/DC.php b/api/src/Page/DC.php index 2fc003a12..31056572a 100644 --- a/api/src/Page/DC.php +++ b/api/src/Page/DC.php @@ -7,334 +7,395 @@ class DC extends Page { - - - public static $arg_list = array('id' => '\d+', 'ids' => '\d+', 'visit' => '\w+\d+-\d+', 's' => '[\w\d-\/]+', 't' => '\w+', 'value' => '.*', 'sid' => '\d+', 'aid' => '\d+', 'pjid' => '\d+', 'imp' => '\d', 'pid' => '\d+', 'h' => '\d\d', 'dmy' => '\d\d\d\d\d\d\d\d', - 'ssid' => '\d+', - 'dcg' => '\d+', - 'a' => '\d+(.\d+)?', - 'b' => '\d+(.\d+)?', - 'c' => '\d+(.\d+)?', - 'al' => '\d+(.\d+)?', - 'be' => '\d+(.\d+)?', - 'ga' => '\d+(.\d+)?', - 'sg' => '\w+', - 'single' => '\d', - 'COMMENTS' => '.*', - - 'dcid' => '\d+', - 'DATACOLLECTIONID' => '\d+', - 'PERSONID' => '\d+', - 'AUTOPROCPROGRAMMESSAGEID' => '\d+', - 'PROCESSINGJOBID' => '\d+', - - 'debug' => '\d', - - ); - - - public static $dispatch = array(array('(/sing:le)(/:id)', 'get', '_data_collections', array('le' => '\w+', 'id' => '\d+')), - array('/chi', 'post', '_chk_image'), - array('/imq/:id', 'get', '_image_qi'), - array('/grid/:id', 'get', '_grid_info'), - array('/grid/xrc/:id', 'get', '_grid_xrc'), - array('/grid/map', 'get', '_grid_map'), - array('/ed/:id', 'get', '_edge', array('id' => '\d+'), 'edge'), - array('/mca/:id', 'get', '_mca', array('id' => '\d+'), 'mca'), - array('/strat/:id', 'get', '_dc_strategies'), - array('/rd/aid/:aid/:id', 'get', '_rd'), - array('/single/t/:t/:id', 'patch', '_set_comment'), - array('/sym', 'get', '_get_symmetry'), - array('/xfm(/:id)', 'get', '_fluo_map'), - - array('/comments(/:dcid)', 'get', '_get_comments'), - array('/comments', 'post', '_add_comment'), - - array('/dat/:id', 'get', '_plot'), - ); - - - - # ------------------------------------------------------------------------ - # Data Collection AJAX Requests - # This is pretty crazy, it will return unioned data collections, energy - # scans, xfe spectra, and robot/sample actions as a single array ordered - # by start time descending for: - # - a proposal / - # - a visit /visit/ - # - a particular sample id /sid/ - # - a project (explicit or implicit) /pjid/(imp/1/) - # - a protein /pid/ - # Its also searchable (A-z0-9-/) and filterable - function _data_collections($single=null) { - session_write_close(); - // $this->db->set_debug(True); - $this->profile('starting dc page'); - #if (!$this->has_arg('visit') && - # !($this->has_arg('sid') && $this->arg('prop')) && - # !($this->has_arg('pjid') && $this->arg('prop'))) - # $this->_error('No visit, sample, or project specified'); - - if (!($this->has_arg('visit') || $this->has_arg('prop'))) $this->_error('No visit or proposal specified'); - - $args = array(); - - $where = ''; - $where2 = ''; - $where3 = ''; - $where4 = ''; - - $sess = array(); - - # Extra joins - $extj = array('','','',''); - # Extra columns - $extc = ''; - $extcg = ''; - - //$this->db->set_debug(True); - # Filter by types - if ($this->has_arg('t')) { - $where = ' AND dc.datacollectionid < 0'; - $where2 = ' AND es.energyscanid < 0'; - $where3 = ' AND r.robotactionid < 0'; - $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; - - if ($this->arg('t') == 'dc' || $this->arg('t') == 'sc' || $this->arg('t') == 'fc' || $this->arg('t') == 'gr') { - - $where = ''; - if ($this->arg('t') == 'sc') $where = ' AND dc.overlap != 0'; - else if ($this->arg('t') == 'gr') $where = ' AND dc.axisrange = 0'; - else if ($this->arg('t') == 'fc') $where = ' AND dc.overlap = 0 AND dc.axisrange > 0'; - - } else if ($this->arg('t') == 'edge') { - $where2 = ''; - - } else if ($this->arg('t') == 'mca') { - $where4 = ''; - - } else if ($this->arg('t') == 'rb') { - $where3 = " AND (r.actiontype LIKE 'LOAD' OR r.actiontype LIKE 'UNLOAD' OR r.actiontype LIKE 'DISPOSE')"; - - } else if ($this->arg('t') == 'ac') { - $where3 = " AND (r.actiontype NOT LIKE 'LOAD' AND r.actiontype NOT LIKE 'UNLOAD' AND r.actiontype NOT LIKE 'DISPOSE')"; - - } else if ($this->arg('t') == 'flag') { - $where = " AND dc.comments LIKE '%_FLAG_%'"; - $where2 = " AND es.comments LIKE '%_FLAG_%'"; - $where4 = " AND xrf.comments LIKE '%_FLAG_%'"; - - } else if ($this->arg('t') == 'ap') { - $where = ' AND app.processingstatus = 1'; - $extj[0] .= "INNER JOIN autoprocintegration ap ON dc.datacollectionid = ap.datacollectionid + public static $arg_list = array( + 'id' => '\d+', + 'ids' => '\d+', + 'visit' => '\w+\d+-\d+', + 's' => '[\w\d\-\/]+', + 't' => '\w+', + 'value' => '.*', + 'sid' => '\d+', + 'aid' => '\d+', + 'pjid' => '\d+', + 'imp' => '\d', + 'pid' => '\d+', + 'h' => '\d\d', + 'dmy' => '\d\d\d\d\d\d\d\d', + 'ssid' => '\d+', + 'dcg' => '\d+', + 'a' => '\d+(.\d+)?', + 'b' => '\d+(.\d+)?', + 'c' => '\d+(.\d+)?', + 'al' => '\d+(.\d+)?', + 'be' => '\d+(.\d+)?', + 'ga' => '\d+(.\d+)?', + 'sg' => '\w+', + 'single' => '\d', + 'COMMENTS' => '.*', + 'dcid' => '\d+', + 'DATACOLLECTIONID' => '\d+', + 'PERSONID' => '\d+', + 'AUTOPROCPROGRAMMESSAGEID' => '\d+', + 'PROCESSINGJOBID' => '\d+', + 'debug' => '\d', + 'sgid' => '\d+' + ); + + public static $dispatch = array( + array('(/sing:le)(/:id)', 'get', '_data_collections', array('le' => '\w+', 'id' => '\d+')), + array('/chi', 'post', '_chk_image'), + array('/imq/:id', 'get', '_image_qi'), + array('/grid/:id', 'get', '_grid_info'), + array('/grid/xrc/:id', 'get', '_grid_xrc'), + array('/grid/map', 'get', '_grid_map'), + array('/ed/:id', 'get', '_edge', array('id' => '\d+'), 'edge'), + array('/mca/:id', 'get', '_mca', array('id' => '\d+'), 'mca'), + array('/strat/:id', 'get', '_dc_strategies'), + array('/rd/aid/:aid/:id', 'get', '_rd'), + array('/single/t/:t/:id', 'patch', '_set_comment'), + array('/sym', 'get', '_get_symmetry'), + array('/xfm(/:id)', 'get', '_fluo_map'), + + array('/comments(/:dcid)', 'get', '_get_comments'), + array('/comments', 'post', '_add_comment'), + + array('/dat/:id', 'get', '_plot'), + ); + + # ------------------------------------------------------------------------ + # Data Collection AJAX Requests + # This is pretty crazy, it will return unioned data collections, energy + # scans, xfe spectra, and robot/sample actions as a single array ordered + # by start time descending for: + # - a proposal / + # - a visit /visit/ + # - a particular sample id /sid/ + # - a project (explicit or implicit) /pjid/(imp/1/) + # - a protein /pid/ + # Its also searchable (A-z0-9-/) and filterable + function _data_collections($single = null) + { + session_write_close(); + // $this->db->set_debug(True); + + $this->profile('starting dc page'); + #if (!$this->has_arg('visit') && + # !($this->has_arg('sid') && $this->arg('prop')) && + # !($this->has_arg('pjid') && $this->arg('prop'))) + # $this->_error('No visit, sample, or project specified'); + + if (!($this->has_arg('visit') || $this->has_arg('prop'))) + $this->_error('No visit or proposal specified'); + + $args = array(); + + $where = ''; + $where2 = ''; + $where3 = ''; + $where4 = ''; + + $sess = array('', '', '', ''); + + # Extra joins + $extj = array('', '', '', ''); + $sample_joins = array( + 'LEFT OUTER JOIN blsample smp ON dc.blsampleid = smp.blsampleid', + 'LEFT OUTER JOIN blsample smp ON es.blsampleid = smp.blsampleid', + 'LEFT OUTER JOIN blsample smp ON r.blsampleid = smp.blsampleid', + 'LEFT OUTER JOIN blsample smp ON xrf.blsampleid = smp.blsampleid' + ); + # Extra columns + $extc = ''; + $extcg = ''; + + $with = ''; + + //$this->db->set_debug(True); + # Filter by types + if ($this->has_arg('t')) { + $where = ' AND dc.datacollectionid < 0'; + $where2 = ' AND es.energyscanid < 0'; + $where3 = ' AND r.robotactionid < 0'; + $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; + if ($this->arg('t') == 'dc' || $this->arg('t') == 'sc' || $this->arg('t') == 'fc' || $this->arg('t') == 'gr') { + + $where = ''; + if ($this->arg('t') == 'sc') + $where = ' AND (dc.overlap != 0 OR dcg.experimentType = "Screening")'; + else if ($this->arg('t') == 'gr') + $where = ' AND dc.axisrange = 0'; + else if ($this->arg('t') == 'fc') + $where = ' AND dc.overlap = 0 AND dc.axisrange > 0 AND dc.numberOfImages > 1 AND dcg.experimentType != "Screening"'; + } else if ($this->arg('t') == 'edge') { + $where2 = ''; + } else if ($this->arg('t') == 'mca') { + $where4 = ''; + } else if ($this->arg('t') == 'rb') { + $where3 = " AND (r.actiontype LIKE 'LOAD' OR r.actiontype LIKE 'UNLOAD' OR r.actiontype LIKE 'DISPOSE')"; + } else if ($this->arg('t') == 'ac') { + $where3 = " AND (r.actiontype NOT LIKE 'LOAD' AND r.actiontype NOT LIKE 'UNLOAD' AND r.actiontype NOT LIKE 'DISPOSE')"; + } else if ($this->arg('t') == 'flag') { + $where = " AND dc.comments LIKE '%_FLAG_%'"; + $where2 = " AND es.comments LIKE '%_FLAG_%'"; + $where4 = " AND xrf.comments LIKE '%_FLAG_%'"; + } else if ($this->arg('t') == 'ap') { + $where = ' AND app.processingstatus = 1'; + $extj[0] .= "INNER JOIN autoprocintegration ap ON dc.datacollectionid = ap.datacollectionid INNER JOIN autoprocprogram app ON app.autoprocprogramid = ap.autoprocprogramid"; - - } else if ($this->arg('t') == 'err') { - $where = " AND appm.autoprocprogrammessageid IS NOT NULL AND (appm.severity = 'WARNING' OR appm.severity = 'ERROR')"; - $extj[0] .= "LEFT OUTER JOIN autoprocintegration api ON dc.datacollectionid = api.datacollectionid - LEFT OUTER JOIN autoprocprogram app ON (app.autoprocprogramid = api.autoprocprogramid OR dc.datacollectionid = app.datacollectionid) + } else if ($this->arg('t') == 'ph') { + $where = " AND app.processingstatus = 1 AND app.processingprograms in ('big_ep', 'fast_ep')"; + $extj[0] .= "INNER JOIN processingjob pj ON dc.datacollectionid = pj.datacollectionid + INNER JOIN autoprocprogram app ON app.processingjobid = pj.processingjobid"; + } else if ($this->arg('t') == 'err') { + $where = " AND appm.autoprocprogrammessageid IS NOT NULL AND (appm.severity = 'WARNING' OR appm.severity = 'ERROR')"; + $extj[0] .= "LEFT OUTER JOIN autoprocintegration api ON dc.datacollectionid = api.datacollectionid + LEFT OUTER JOIN processingjob pj ON dc.datacollectionid = pj.datacollectionid + LEFT OUTER JOIN autoprocprogram app ON (app.autoprocprogramid = api.autoprocprogramid OR dc.datacollectionid = pj.datacollectionid) INNER JOIN autoprocprogrammessage appm ON appm.autoprocprogramid = app.autoprocprogramid"; - } - } - - # Pagination - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; + } else if ($this->arg('t') == "scrystal" || $this->arg('t') == "nscrystal") { + // Single crystal or explicitly non-single-crystal fields + $where = ($this->arg('t') == "nscrystal") ? ' AND NOT ' : ' AND '; + // This IS NOT NULL is not redundant; this condition always evalutes to TRUE with AND NOT without it + $where .= '(dcg.experimentType IS NOT NULL AND dcg.experimentType in ("OSC", "Diamond Anvil High Pressure"))'; } + } + + # Pagination + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + + # Check that whatever we are looking for actually exists + $info = array(); + # Visits + if ($this->has_arg('visit')) { + $info = $this->db->pq("SELECT TO_CHAR(s.startdate, 'HH24') as sh, TO_CHAR(s.startdate, 'DDMMYYYY') as dmy, s.sessionid, s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1", array($this->arg('visit'))); + + if (!sizeof($info)) { + $this->_error('No such visit'); + } else + $info = $info[0]; + + $sess = array('dcg.sessionid=:1', 'es.sessionid=:2', 'r.blsessionid=:3', 'xrf.sessionid=:4'); + for ($i = 0; $i < 4; $i++) + array_push($args, $info['SESSIONID']); - - # Check that whatever we are looking for actually exists - $info = array(); - # Visits - if ($this->has_arg('visit')) { - $info = $this->db->pq("SELECT TO_CHAR(s.startdate, 'HH24') as sh, TO_CHAR(s.startdate, 'DDMMYYYY') as dmy, s.sessionid, s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1", array($this->arg('visit'))); - - if (!sizeof($info)) { - $this->_error('No such visit'); - } else $info = $info[0]; - - $sess = array('dcg.sessionid=:1', 'es.sessionid=:2', 'r.blsessionid=:3', 'xrf.sessionid=:4'); - for ($i = 0; $i < 4; $i++) array_push($args, $info['SESSIONID']); - # Subsamples - } else if ($this->has_arg('ssid') && $this->has_arg('prop')) { - $info = $this->db->pq("SELECT ss.blsubsampleid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE ss.blsubsampleid=:1 AND CONCAT(p.proposalcode, p.proposalnumber) LIKE :2", array($this->arg('ssid'), $this->arg('prop'))); - - $tables2 = array('dc', 'es', 'r', 'xrf'); - $ct = 0; - foreach ($tables2 as $i => $t) { - if ($t == 'r') $sess[$i] = 'r.robotactionid<1'; - else { - $sess[$i] = $t.'.blsubsampleid=:'.($ct+1); - $ct++; - } + } else if ($this->has_arg('ssid') && $this->has_arg('prop')) { + $info = $this->db->pq("SELECT ss.blsubsampleid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE ss.blsubsampleid=:1 AND CONCAT(p.proposalcode, p.proposalnumber) LIKE :2", array($this->arg('ssid'), $this->arg('prop'))); + + $tables2 = array('dc', 'es', 'r', 'xrf'); + $ct = 0; + foreach ($tables2 as $i => $t) { + if ($t == 'r') + $sess[$i] = 'r.robotactionid<1'; + else { + $sess[$i] = $t . '.blsubsampleid=:' . ($ct + 1); + $ct++; } - for ($i = 0; $i < 3; $i++) array_push($args, $this->arg('ssid')); + } + for ($i = 0; $i < 3; $i++) + array_push($args, $this->arg('ssid')); # Samples - } else if ($this->has_arg('sid') && $this->has_arg('prop')) { - $info = $this->db->pq("SELECT s.blsampleid FROM blsample s INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE s.blsampleid=:1 AND CONCAT(p.proposalcode, p.proposalnumber) LIKE :2", array($this->arg('sid'), $this->arg('prop'))); - - $tables2 = array('dc', 'es', 'r', 'xrf'); - foreach ($tables2 as $i => $t) $sess[$i] = $t.'.blsampleid=:'.($i+1); - for ($i = 0; $i < 4; $i++) array_push($args, $this->arg('sid')); - + } else if ($this->has_arg('sid') && $this->has_arg('prop')) { + $info = $this->db->pq("SELECT s.blsampleid FROM blsample s INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE s.blsampleid=:1 AND CONCAT(p.proposalcode, p.proposalnumber) LIKE :2", array($this->arg('sid'), $this->arg('prop'))); + + $tables2 = array('dc', 'es', 'r', 'xrf'); + foreach ($tables2 as $i => $t) + $sess[$i] = $t . '.blsampleid=:' . ($i + 1); + for ($i = 0; $i < 4; $i++) + array_push($args, $this->arg('sid')); + # Projects - } else if ($this->has_arg('pjid')) { - $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personid, $this->user->personid)); - - $tables = array(array('project_has_dcgroup', 'dc', 'datacollectiongroupid'), - array('project_has_energyscan', 'es', 'energyscanid'), - array('project_has_session', 'r', 'blsessionid', 'sessionid'), - array('project_has_xfefspectrum', 'xrf', 'xfefluorescencespectrumid'), - ); - - $extc = "CONCAT(CONCAT(CONCAT(prop.proposalcode,prop.proposalnumber), '-'), ses.visit_number) as vis, CONCAT(prop.proposalcode, prop.proposalnumber) as prop, "; - $extcg = "max(CONCAT(CONCAT(CONCAT(prop.proposalcode,prop.proposalnumber), '-'), ses.visit_number)) as vis, max(CONCAT(prop.proposalcode, prop.proposalnumber)) as prop, "; - if ($this->has_arg('dcg')) $extcg = $extc; - - foreach ($tables as $i => $t) { - $ct = sizeof($t) == 4 ? $t[3] : $t[2]; - - $extj[$i] .= " LEFT OUTER JOIN $t[0] prj ON $t[1].$t[2] = prj.$ct INNER JOIN proposal prop ON ses.proposalid = prop.proposalid"; - $sess[$i] = 'prj.projectid=:'.($i+1); - - if ($this->has_arg('imp')) { - if ($this->arg('imp')) { - $extj[$i] .= " LEFT OUTER JOIN crystal cr ON cr.crystalid = smp.crystalid LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN project_has_protein prj2 ON prj2.proteinid = pr.proteinid LEFT OUTER JOIN project_has_blsample prj3 ON prj3.blsampleid = smp.blsampleid"; - $sess[$i] = '(prj.projectid=:'.($i*3+1).' OR prj2.projectid=:'.($i*3+2).' OR prj3.projectid=:'.($i*3+3).')'; - } + } else if ($this->has_arg('pjid')) { + $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personId, $this->user->personId)); + + $tables = array( + array('project_has_dcgroup', 'dc', 'datacollectiongroupid'), + array('project_has_energyscan', 'es', 'energyscanid'), + array('project_has_session', 'r', 'blsessionid', 'sessionid'), + array('project_has_xfefspectrum', 'xrf', 'xfefluorescencespectrumid'), + ); + + $extc = "CONCAT(prop.proposalcode, prop.proposalnumber, '-', ses.visit_number) as vis, CONCAT(prop.proposalcode, prop.proposalnumber) as prop, "; + $extcg = "max(CONCAT(prop.proposalcode, prop.proposalnumber, '-', ses.visit_number)) as vis, max(CONCAT(prop.proposalcode, prop.proposalnumber)) as prop, "; + if ($this->has_arg('dcg')) + $extcg = $extc; + + foreach ($tables as $i => $t) { + $ct = sizeof($t) == 4 ? $t[3] : $t[2]; + + $extj[$i] .= " LEFT OUTER JOIN $t[0] prj ON $t[1].$t[2] = prj.$ct INNER JOIN proposal prop ON ses.proposalid = prop.proposalid"; + $sess[$i] = 'prj.projectid=:' . ($i + 1); + + if ($this->has_arg('imp')) { + if ($this->arg('imp')) { + $extj[$i] .= " LEFT OUTER JOIN crystal cr ON cr.crystalid = smp.crystalid LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN project_has_protein prj2 ON prj2.proteinid = pr.proteinid LEFT OUTER JOIN project_has_blsample prj3 ON prj3.blsampleid = smp.blsampleid"; + $sess[$i] = '(prj.projectid=:' . ($i * 3 + 1) . ' OR prj2.projectid=:' . ($i * 3 + 2) . ' OR prj3.projectid=:' . ($i * 3 + 3) . ')'; } } - - - $n = 4; - if ($this->has_arg('imp')) - if ($this->arg('imp')) $n = 12; - for ($i = 0; $i < $n; $i++) array_push($args, $this->arg('pjid')); - - + } + + + $n = 4; + if ($this->has_arg('imp')) + if ($this->arg('imp')) + $n = 12; + for ($i = 0; $i < $n; $i++) + array_push($args, $this->arg('pjid')); + + # Proteins - } else if ($this->has_arg('pid')) { - $info = $this->db->pq("SELECT proteinid FROM protein p WHERE p.proteinid=:1", array($this->arg('pid'))); + } else if ($this->has_arg('pid')) { + $info = $this->db->pq("SELECT proteinid FROM protein p WHERE p.proteinid=:1", array($this->arg('pid'))); - foreach (array('dc', 'es', 'r', 'xrf') as $i => $t) { - $extj[$i] .= " INNER JOIN crystal cr ON cr.crystalid = smp.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid"; - $sess[$i] = 'pr.proteinid=:'.(sizeof($args)+1); - array_push($args, $this->arg('pid')); - } + foreach (array('dc', 'es', 'r', 'xrf') as $i => $t) { + $extj[$i] .= " INNER JOIN crystal cr ON cr.crystalid = smp.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid"; + $sess[$i] = 'pr.proteinid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('pid')); + } # Processing job - } else if ($this->has_arg('PROCESSINGJOBID')) { - $info = $this->db->pq('SELECT processingjobid + } else if ($this->has_arg('PROCESSINGJOBID')) { + $info = $this->db->pq('SELECT processingjobid FROM processingjob pj INNER JOIN datacollection dc ON pj.datacollectionid = dc.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE pj.processingjobid=:1 AND s.proposalid=:2', array($this->arg('PROCESSINGJOBID'), $this->proposalid)); - $where2 .= ' AND es.energyscanid < 0'; - $where3 .= ' AND r.robotactionid < 0'; - $where4 .= ' AND xrf.xfefluorescencespectrumid < 0'; + $where2 .= ' AND es.energyscanid < 0'; + $where3 .= ' AND r.robotactionid < 0'; + $where4 .= ' AND xrf.xfefluorescencespectrumid < 0'; - for ($i = 0; $i < 4; $i++) { - $sess[$i] = 'ses.proposalid=:'.($i+1); - array_push($args, $this->proposalid); - } + for ($i = 0; $i < 4; $i++) { + $sess[$i] = 'ses.proposalid=:' . ($i + 1); + array_push($args, $this->proposalid); + } - $where .= ' AND pjis.processingjobid=:'.(sizeof($args)+1); - array_push($args, $this->arg('PROCESSINGJOBID')); - $extj[0] .= ' LEFT OUTER JOIN processingjobimagesweep pjis ON pjis.datacollectionid=dc.datacollectionid'; + $where .= ' AND pjis.processingjobid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('PROCESSINGJOBID')); + $extj[0] .= ' LEFT OUTER JOIN processingjobimagesweep pjis ON pjis.datacollectionid=dc.datacollectionid'; + } else if ($this->has_arg('sgid')) { + $info = $this->db->pq('SELECT blsampleid + FROM blsamplegroup_has_blsample bshg + LEFT OUTER JOIN blsamplegroup bsg ON bsg.blsamplegroupid = bshg.blsamplegroupid + WHERE bsg.blsamplegroupid=:1 AND bsg.proposalid=:2', array($this->arg('sgid'), $this->proposalid)); + + $with .= "WITH samples AS (SELECT sample.* FROM blsamplegroup_has_blsample bshg LEFT OUTER JOIN BLSample sample ON sample.blsampleid = bshg.blsampleid WHERE bshg.blsamplegroupid = {$this->arg('sgid')})"; + $sample_joins[0] = 'JOIN samples smp ON smp.blsampleid = dc.blsampleid'; + $sample_joins[1] = 'JOIN samples smp ON smp.blsampleid = es.blsampleid'; + $sample_joins[2] = 'JOIN samples smp ON smp.blsampleid = r.blsampleid'; + $sample_joins[3] = 'JOIN samples smp ON smp.blsampleid = xrf.blsampleid'; + + $sess[0] = '1=1'; + $sess[1] = '1=1'; + $sess[2] = '1=1'; + $sess[3] = '1=1'; # Proposal - } else if ($this->has_arg('prop')) { - $info = $this->db->pq('SELECT proposalid FROM proposal p WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1', array($this->arg('prop'))); - - for ($i = 0; $i < 4; $i++) { - $sess[$i] = 'ses.proposalid=:'.($i+1); - array_push($args, $this->proposalid); - } + } else if ($this->has_arg('prop')) { + $info = $this->db->pq('SELECT proposalid FROM proposal p WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1', array($this->arg('prop'))); + + for ($i = 0; $i < 4; $i++) { + $sess[$i] = 'ses.proposalid=:' . ($i + 1); + array_push($args, $this->proposalid); } - - if (!sizeof($info)) $this->_error('The specified visit, sample, or project doesnt exist'); - - - # Filter by time for visits - if (($this->has_arg('h') && ($this->has_arg('visit') || $this->has_arg('dmy'))) || $this->has_arg('dmy')) { - $where .= "AND dc.starttime > TO_DATE(:".(sizeof($args)+1).", 'HH24:MI:SS DDMMYYYY') AND dc.starttime < TO_DATE(:".(sizeof($args)+2).", 'HH24:MI:SS DDMMYYYY')"; - $where2 .= "AND es.starttime > TO_DATE(:".(sizeof($args)+3).", 'HH24:MI:SS DDMMYYYY') AND es.starttime < TO_DATE(:".(sizeof($args)+4).", 'HH24:MI:SS DDMMYYYY')"; - $where3 .= "AND r.starttimestamp > TO_DATE(:".(sizeof($args)+5).", 'HH24:MI:SS DDMMYYYY') AND r.starttimestamp < TO_DATE(:".(sizeof($args)+6).", 'HH24:MI:SS DDMMYYYY')"; - $where4 .= "AND xrf.starttime > TO_DATE(:".(sizeof($args)+7).", 'HH24:MI:SS DDMMYYYY') AND xrf.starttime < TO_DATE(:".(sizeof($args)+8).", 'HH24:MI:SS DDMMYYYY')"; - - if ($this->has_arg('dmy')) { - $my = $this->arg('dmy'); - } else { - $my = $info['DMY']; - if ($this->arg('h') < $info['SH']) { - $sd = mktime(0,0,0,substr($my,2,2), substr($my,0,2), substr($my,4,4))+(3600*24); - $my = date('dmY', $sd); - } - } - - - if ($this->has_arg('h')) { - $st = $this->arg('h').':00:00 '; - $en = $this->arg('h').':59:59 '; - } else { - $st = '00:00:00'; - $en = '23:59:59 '; - } - - for ($i = 0; $i < 4; $i++) { - array_push($args, $st.$my); - array_push($args, $en.$my); - } + } + + if (!sizeof($info)) { + if ($this->has_arg('sgid')) { + $this->_error('The specified sample group does not exist or have any samples added to it'); + } else { + $this->_error('The specified visit, sample or project doesnt exist'); } - - - # If not staff check they have access to data collection - if (!$this->has_arg('visit') && !$this->staff) { - for ($i = 0; $i < 4; $i++) { - $extj[$i] .= " INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:".(sizeof($args)+1); - array_push($args, $this->user->personid); + } + + # Filter by time for visits + if (($this->has_arg('h') && ($this->has_arg('visit') || $this->has_arg('dmy'))) || $this->has_arg('dmy')) { + $where .= "AND dc.starttime > TO_DATE(:" . (sizeof($args) + 1) . ", 'HH24:MI:SS DDMMYYYY') AND dc.starttime < TO_DATE(:" . (sizeof($args) + 2) . ", 'HH24:MI:SS DDMMYYYY')"; + $where2 .= "AND es.starttime > TO_DATE(:" . (sizeof($args) + 3) . ", 'HH24:MI:SS DDMMYYYY') AND es.starttime < TO_DATE(:" . (sizeof($args) + 4) . ", 'HH24:MI:SS DDMMYYYY')"; + $where3 .= "AND r.starttimestamp > TO_DATE(:" . (sizeof($args) + 5) . ", 'HH24:MI:SS DDMMYYYY') AND r.starttimestamp < TO_DATE(:" . (sizeof($args) + 6) . ", 'HH24:MI:SS DDMMYYYY')"; + $where4 .= "AND xrf.starttime > TO_DATE(:" . (sizeof($args) + 7) . ", 'HH24:MI:SS DDMMYYYY') AND xrf.starttime < TO_DATE(:" . (sizeof($args) + 8) . ", 'HH24:MI:SS DDMMYYYY')"; + + if ($this->has_arg('dmy')) { + $my = $this->arg('dmy'); + } else { + $my = $info['DMY']; + if ($this->arg('h') < $info['SH']) { + $sd = mktime(0, 0, 0, substr($my, 2, 2), substr($my, 0, 2), substr($my, 4, 4)) + (3600 * 24); + $my = date('dmY', $sd); } } - - # View a single data collection - if ($this->has_arg('id')) { - $st = sizeof($args)+1; - $where .= ' AND dc.datacollectionid=:'.$st; - $where3 .= ' AND r.robotactionid=:'.($st+1); - $where2 .= ' AND es.energyscanid=:'.($st+2); - $where4 .= ' AND xrf.xfefluorescencespectrumid=:'.($st+3); - for ($i = 0; $i < 4; $i++) array_push($args, $this->arg('id')); + + + if ($this->has_arg('h')) { + $st = $this->arg('h') . ':00:00 '; + $en = $this->arg('h') . ':59:59 '; + } else { + $st = '00:00:00'; + $en = '23:59:59 '; } - - # Search terms - if ($this->has_arg('s')) { - $s = str_replace('_', '$_', $this->arg('s')); - - $st = sizeof($args) + 1; - $where .= " AND (lower(dc.filetemplate) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(dc.imagedirectory) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%', :".($st+2)."), '%')) ESCAPE '$')"; - $where2 .= " AND (lower(es.comments) LIKE lower(CONCAT(CONCAT('%',:".($st+3)."), '%')) ESCAPE '$' OR lower(es.element) LIKE lower(CONCAT(CONCAT('%',:".($st+4)."), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:".($st+5)."), '%')) ESCAPE '$')"; - $where3 .= ' AND r.robotactionid < 0'; - $where4 .= " AND (lower(xrf.filename) LIKE lower(CONCAT(CONCAT('%',:".($st+6)."), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:".($st+7)."), '%')) ESCAPE '$')"; - - for ($i = 0; $i < 8; $i++) array_push($args, $s); + + for ($i = 0; $i < 4; $i++) { + array_push($args, $st . $my); + array_push($args, $en . $my); } - + } + + # If not staff check they have access to data collection + if (!$this->has_arg('visit') && !$this->staff) { + for ($i = 0; $i < 4; $i++) { + $extj[$i] .= " INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:" . (sizeof($args) + 1); + array_push($args, $this->user->personId); + } + } + + # View a single data collection + if ($this->has_arg('id')) { + $st = sizeof($args) + 1; + $where .= ' AND dc.datacollectionid=:' . $st; + $where3 .= ' AND r.robotactionid=:' . ($st + 1); + $where2 .= ' AND es.energyscanid=:' . ($st + 2); + $where4 .= ' AND xrf.xfefluorescencespectrumid=:' . ($st + 3); + for ($i = 0; $i < 4; $i++) + array_push($args, $this->arg('id')); + } + + # Search terms + if ($this->has_arg('s')) { + $s = str_replace('_', '$_', $this->arg('s')); + + $st = sizeof($args) + 1; + $where .= " AND (lower(dc.filetemplate) LIKE lower(CONCAT(CONCAT('%',:$st),'%')) ESCAPE '$' OR lower(dc.imagedirectory) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%', :" . ($st + 2) . "), '%')) ESCAPE '$')"; + $where2 .= " AND (lower(es.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 3) . "), '%')) ESCAPE '$' OR lower(es.element) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 4) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 5) . "), '%')) ESCAPE '$')"; + $where3 .= ' AND r.robotactionid < 0'; + $where4 .= " AND (lower(xrf.filename) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 6) . "), '%')) ESCAPE '$' OR lower(smp.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 7) . "), '%')) ESCAPE '$')"; + + for ($i = 0; $i < 8; $i++) + array_push($args, $s); + } + + # Set Count field + if ($this->has_arg('dcg') || $this->has_arg('PROCESSINGJOBID') || $this->has_arg('sgid')) { + $count_field = 'dc.datacollectionid'; + } else { + $count_field = 'distinct dc.datacollectiongroupid'; + } - # Data collection group - if ($this->has_arg('dcg') || $this->has_arg('PROCESSINGJOBID')) { - $count_field = 'dc.datacollectionid'; - $fields = "count(distinct dca.datacollectionfileattachmentid) as dcac, + # Data collection group + if ($this->has_arg('dcg') || $this->has_arg('PROCESSINGJOBID')) { + $fields = "count(distinct dca.datacollectionfileattachmentid) as dcac, + if(dca.fileType='recip',1,0) as recip, count(distinct dcc.datacollectioncommentid) as dccc, 1 as dcc, smp.name as sample, @@ -388,12 +449,12 @@ function _data_collections($single=null) { dc.objaperture, dc.magnification, dc.totalexposeddose as totaldose, + CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, d.detectormanufacturer, d.detectormodel, TIMESTAMPDIFF('MINUTE', dc.starttime, CURRENT_TIMESTAMP) as age, - dc.numberofpasses, dc.c1aperture, dc.c3aperture, @@ -417,7 +478,7 @@ function _data_collections($single=null) { ses.archived, IFNULL(dc.rotationaxis, 'Omega') as rotationaxis, dc.detector2theta"; - $groupby = 'GROUP BY smp.name, + $groupby = 'GROUP BY smp.name, smp.blsampleid, ses.visit_number, dc.kappastart, @@ -455,21 +516,20 @@ function _data_collections($single=null) { dc.ybeam, dc.chistart '; - // $this->db->set_debug(True); - - // will want to support these too at some point - $where2 = ' AND es.energyscanid < 0'; - $where3 = ' AND r.robotactionid < 0'; - $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; + // $this->db->set_debug(True); - if ($this->has_arg('dcg')) { - $where .= ' AND dc.datacollectiongroupid=:'.(sizeof($args)+1); - array_push($args, $this->arg('dcg')); - } + // will want to support these too at some point + $where2 = ' AND es.energyscanid < 0'; + $where3 = ' AND r.robotactionid < 0'; + $where4 = ' AND xrf.xfefluorescencespectrumid < 0'; - } else { - $count_field = 'distinct dc.datacollectiongroupid'; - $fields = "count(distinct dca.datacollectionfileattachmentid) as dcac, + if ($this->has_arg('dcg')) { + $where .= ' AND dc.datacollectiongroupid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('dcg')); + } + } else { + $fields = "count(distinct dca.datacollectionfileattachmentid) as dcac, + if(dca.fileType='recip',1,0) as recip, count(distinct dcc.datacollectioncommentid) as dccc, count(distinct dc.datacollectionid) as dcc, min(smp.name) as sample, @@ -510,7 +570,7 @@ function _data_collections($single=null) { min(dc.xtalsnapshotfullpath2) as x2, min(dc.xtalsnapshotfullpath3) as x3, min(dc.xtalsnapshotfullpath4) as x4, - min(dc.starttime) as sta, + max(dc.starttime) as sta, min(dc.detectordistance) as det, min(dc.xbeam) as xbeam, min(dc.ybeam) as ybeam, @@ -523,6 +583,7 @@ function _data_collections($single=null) { max(dc.objaperture) as objaperture, max(dc.magnification) as magnification, sum(dc.totalabsorbeddose) as totaldose, + CAST(dc.totalabsorbeddose AS DECIMAL(5, 2)) as totalabsdose, max(d.detectormanufacturer) as detectormanufacturer, max(d.detectormodel) as detectormodel, max(d.detectorpixelsizehorizontal) as detectorpixelsizehorizontal, @@ -551,51 +612,62 @@ function _data_collections($single=null) { max(ses.archived) as archived, IFNULL(max(dc.rotationaxis), 'Omega') as rotationaxis, dc.detector2theta"; - $groupby = "GROUP BY dc.datacollectiongroupid"; - } - - - $tot = $this->db->pq("SELECT sum(tot) as t FROM (SELECT count($count_field) as tot FROM datacollection dc - INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - INNER JOIN blsession ses ON ses.sessionid = dcg.sessionid - LEFT OUTER JOIN blsample smp ON dc.blsampleid = smp.blsampleid - $extj[0] - WHERE $sess[0] $where - - UNION SELECT count(es.energyscanid) as tot FROM energyscan es - INNER JOIN blsession ses ON ses.sessionid = es.sessionid - LEFT OUTER JOIN blsample smp ON es.blsampleid = smp.blsampleid - $extj[1] - WHERE $sess[1] $where2 - - UNION SELECT count(xrf.xfefluorescencespectrumid) as tot from xfefluorescencespectrum xrf - INNER JOIN blsession ses ON ses.sessionid = xrf.sessionid - LEFT OUTER JOIN blsample smp ON xrf.blsampleid = smp.blsampleid - $extj[3] - WHERE $sess[3] $where4 - - UNION SELECT count(r.robotactionid) as tot FROM robotaction r - INNER JOIN blsession ses ON ses.sessionid = r.blsessionid - LEFT OUTER JOIN blsample smp ON r.blsampleid = smp.blsampleid - $extj[2] - WHERE $sess[2] $where3) inq", $args); - $tot = $tot[0]['T']; - - $this->profile('after page count'); - - $pgs = intval($tot/$pp); - if ($tot % $pp != 0) $pgs++; - - $st = sizeof($args) + 1; - array_push($args, $start); - array_push($args, $end); - + $groupby = "GROUP BY dc.datacollectiongroupid"; + } - $q = "SELECT $extcg $fields + $total_query = "SELECT sum(tot) as t FROM ( + $with + SELECT count($count_field) as tot + FROM datacollection dc + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession ses ON ses.sessionid = dcg.sessionid + $sample_joins[0] + $extj[0] + WHERE $sess[0] $where + + UNION SELECT count(es.energyscanid) as tot + FROM energyscan es + INNER JOIN blsession ses ON ses.sessionid = es.sessionid + $sample_joins[1] + $extj[1] + WHERE $sess[1] $where2 + + UNION SELECT count(xrf.xfefluorescencespectrumid) as tot + FROM xfefluorescencespectrum xrf + INNER JOIN blsession ses ON ses.sessionid = xrf.sessionid + $sample_joins[3] + $extj[3] + WHERE $sess[3] $where4 + + UNION SELECT count(r.robotactionid) as tot + FROM robotaction r + INNER JOIN blsession ses ON ses.sessionid = r.blsessionid + $sample_joins[2] + $extj[2] + WHERE $sess[2] $where3 + ) inq"; + + $tot = $this->db->pq($total_query, $args); + $tot = $tot[0]['T']; + + $this->profile('after page count'); + + $pgs = intval($tot / $pp); + if ($tot % $pp != 0) + $pgs++; + + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); + + + $q = " + $with + SELECT $extcg $fields FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession ses ON ses.sessionid = dcg.sessionid - LEFT OUTER JOIN blsample smp ON dc.blsampleid = smp.blsampleid + $sample_joins[0] LEFT OUTER JOIN datacollectioncomment dcc ON dc.datacollectionid = dcc.datacollectionid LEFT OUTER JOIN datacollectionfileattachment dca ON dc.datacollectionid = dca.datacollectionid LEFT OUTER JOIN detector d ON d.detectorid = dc.detectorid @@ -604,10 +676,11 @@ function _data_collections($single=null) { WHERE $sess[0] $where $groupby - UNION + UNION ALL SELECT $extc 1 as dcac, + 0 as recip, 1 as dccc, 1 as dcc, smp.name as sample, @@ -662,6 +735,7 @@ function _data_collections($single=null) { 1, 1, 1, + 1, '', '', TIMESTAMPDIFF(MINUTE, es.starttime, CURRENT_TIMESTAMP) as age, @@ -691,7 +765,7 @@ function _data_collections($single=null) { '' FROM energyscan es INNER JOIN blsession ses ON ses.sessionid = es.sessionid - LEFT OUTER JOIN blsample smp ON es.blsampleid = smp.blsampleid + $sample_joins[1] $extj[1] WHERE $sess[1] $where2 @@ -699,6 +773,7 @@ function _data_collections($single=null) { SELECT $extc 1 as dcac, + 0 as recip, 1 as dccc, 1 as dcc, smp.name as sample, @@ -753,6 +828,7 @@ function _data_collections($single=null) { 1, 1, 1, + 1, '', '', TIMESTAMPDIFF('MINUTE', xrf.starttime, CURRENT_TIMESTAMP) as age, @@ -782,7 +858,7 @@ function _data_collections($single=null) { '' FROM xfefluorescencespectrum xrf INNER JOIN blsession ses ON ses.sessionid = xrf.sessionid - LEFT OUTER JOIN blsample smp ON xrf.blsampleid = smp.blsampleid + $sample_joins[3] $extj[3] WHERE $sess[3] $where4 @@ -790,6 +866,7 @@ function _data_collections($single=null) { SELECT $extc 1 as dcac, + 0 as recip, 1 as dccc, 1 as dcc, smp.name as sample, @@ -844,6 +921,7 @@ function _data_collections($single=null) { 1, 1, 1, + 1, '', '', TIMESTAMPDIFF('MINUTE', r.starttimestamp, CURRENT_TIMESTAMP) as age, @@ -873,279 +951,299 @@ function _data_collections($single=null) { '' FROM robotaction r INNER JOIN blsession ses ON ses.sessionid = r.blsessionid - LEFT OUTER JOIN blsample smp ON r.blsampleid = smp.blsampleid + $sample_joins[2] $extj[2] WHERE $sess[2] $where3 ORDER BY sta DESC, id DESC"; - - $dcs = $this->db->paginate($q, $args); - $this->profile('main query'); - - foreach ($dcs as $i => &$dc) { - $dc['SN'] = 0; - $dc['DI'] = 0; - - #if ($this->has_arg('sid') || $this->has_arg('pjid')) $dc['VIS'] = $this->arg('prop').'-'.$dc['VN']; - if ($this->has_arg('sid')) $dc['VIS'] = $this->arg('prop').'-'.$dc['VN']; - - foreach (array('X1', 'X2', 'X3', 'X4') as $x) { - $dc[$x] = file_exists($dc[$x]) ? 1 : 0; - } - - // Data collections - if ($dc['TYPE'] == 'data') { - $nf = array(1 => array('AXISSTART'), 2 => array('RESOLUTION', 'TRANSMISSION', 'AXISRANGE'), 4 => array('WAVELENGTH', 'EXPOSURETIME')); - - $dc['DIRFULL'] = $dc['DIR']; - $dc['DIR'] = preg_replace('/.*\/'.$this->arg('prop').'-'.$dc['VN'].'\//', '', $dc['DIR']); - - $dc['BSX'] = round($dc['BSX']*1000); - $dc['BSY'] = round($dc['BSY']*1000); - - if (!$dc['DCT']) { - if ($dc['AXISRANGE'] == 0) $dc['DCT'] = 'Still Image'; - if ($dc['AXISRANGE'] == 0 && $dc['NI'] > 1) $dc['DCT'] = 'Grid Scan'; - if ($dc['OVERLAP'] != 0) $dc['DCT'] = 'Screening'; - if ($dc['AXISRANGE'] > 0 && $dc['OVERLAP'] == 0) $dc['DCT'] = 'Data Collection'; - } + $dcs = $this->db->paginate($q, $args); + $this->profile('main query'); - if ($dc['DCT'] == 'Mesh') $dc['DCT'] = 'Grid Scan'; - if ($dc['DCT'] == 'OSC') $dc['DCT'] = 'Data Collection'; - + foreach ($dcs as $i => &$dc) { + $dc['SN'] = 0; + $dc['DI'] = 0; + + #if ($this->has_arg('sid') || $this->has_arg('pjid')) $dc['VIS'] = $this->arg('prop').'-'.$dc['VN']; + if ($this->has_arg('sid')) + $dc['VIS'] = $this->arg('prop') . '-' . $dc['VN']; + + foreach (array('X1', 'X2', 'X3', 'X4') as $x) { + $dc[$x] = file_exists($dc[$x]) ? 1 : 0; + } + + // Data collections + if ($dc['TYPE'] == 'data') { + $nf = array(1 => array('AXISSTART'), 2 => array('RESOLUTION', 'TRANSMISSION', 'AXISRANGE'), 4 => array('WAVELENGTH', 'EXPOSURETIME')); + + $dc['DIRFULL'] = $dc['DIR']; + $dc['DIR'] = preg_replace('/.*\/' . $this->arg('prop') . '-' . $dc['VN'] . '\//', '', $dc['DIR']); + + $dc['BSX'] = round($dc['BSX'] * 1000); + $dc['BSY'] = round($dc['BSY'] * 1000); + + if (!$dc['DCT']) { + if ($dc['AXISRANGE'] == 0) + $dc['DCT'] = 'Still Image'; + if ($dc['AXISRANGE'] == 0 && $dc['NI'] > 1) + $dc['DCT'] = 'Grid Scan'; + if ($dc['OVERLAP'] != 0) + $dc['DCT'] = 'Screening'; + if ($dc['AXISRANGE'] > 0 && $dc['OVERLAP'] == 0) + $dc['DCT'] = 'Data Collection'; + } + + if ($dc['DCT'] == 'Mesh') + $dc['DCT'] = 'Grid Scan'; + if ($dc['DCT'] != 'Serial Fixed' && $dc['DCT'] != 'Serial Jet' && $dc['AXISRANGE'] == 0 && $dc['NI'] > 1) { + $dc['TYPE'] = 'grid'; + } + //$this->profile('dc'); - if ($dc['AXISRANGE'] == 0 && $dc['NI'] > 1) $dc['TYPE'] = 'grid'; - //$this->profile('dc'); - // Edge Scans - } else if ($dc['TYPE'] == 'edge') { - $dc['EPK'] = floatVal($dc['EPK']); - $dc['EIN'] = floatVal($dc['EIN']); - - # Transmission factor rather than transmission :( - $dc['TRANSMISSION'] *= 100; - - $dc['FILETEMPLATE'] = preg_replace('/.*\/'.$this->arg('prop').'-'.$dc['VN'].'\//', '', $dc['FILETEMPLATE']); - - $nf = array(2 => array('EXPOSURETIME'), 2 => array('AXISSTART', 'RESOLUTION', 'TRANSMISSION')); - $this->profile('edge'); - + } else if ($dc['TYPE'] == 'edge') { + $dc['EPK'] = floatVal($dc['EPK']); + $dc['EIN'] = floatVal($dc['EIN']); + + # Transmission factor rather than transmission :( + $dc['TRANSMISSION'] *= 100; + + $dc['FILETEMPLATE'] = preg_replace('/.*\/' . $this->arg('prop') . '-' . $dc['VN'] . '\//', '', $dc['FILETEMPLATE']); + + $nf = array(2 => array('EXPOSURETIME', 'AXISSTART', 'RESOLUTION', 'TRANSMISSION')); + $this->profile('edge'); + // MCA Scans - } else if ($dc['TYPE'] == 'mca') { - $nf = array(2 => array('EXPOSURETIME', 'WAVELENGTH', 'TRANSMISSION')); - $dc['DIRFULL'] = $dc['DIR']; - $dc['DIR'] = preg_replace('/.*\/\d\d\d\d\/\w\w\d+-\d+\//', '', $dc['DIR']); - + } else if ($dc['TYPE'] == 'mca') { + $nf = array(2 => array('EXPOSURETIME', 'WAVELENGTH', 'TRANSMISSION')); + $dc['DIRFULL'] = $dc['DIR']; + $dc['DIR'] = preg_replace('/.*\/\d\d\d\d\/\w\w\d+-\d+\//', '', $dc['DIR']); + // Robot loads - } else if ($dc['TYPE'] == 'load') { - $nf = array(); - if ($dc['IMP'] == 'ANNEAL' || $dc['IMP'] == 'WASH') $dc['TYPE'] = 'action'; - } - - - //$dc['AP'] = array(0,0,0,0,0,0,0,0); - - foreach ($nf as $nff => $cols) { - foreach ($cols as $c) { - $dc[$c] = number_format($dc[$c], $nff); - } - } - + } else if ($dc['TYPE'] == 'load') { + $nf = array(); + if ($dc['IMP'] == 'ANNEAL' || $dc['IMP'] == 'WASH') + $dc['TYPE'] = 'action'; } - $this->profile('processing'); - if ($single) $this->_output($dcs[0]); - else $this->_output(array($pgs, $dcs)); - } - - # ------------------------------------------------------------------------ - # Check whether diffraction and snapshot images exist - function _chk_image() { - global $jpeg_thumb_location; - if (!($this->has_arg('visit') || $this->has_arg('prop'))) $this->_error('No visit or proposal specified'); - - $where = array(); - $ids = array(); - if ($this->has_arg('ids')) { - if (is_array($this->arg('ids'))) { - foreach ($this->arg('ids') as $i) { - array_push($ids,$i); - array_push($where,'dc.datacollectionid=:'.sizeof($ids)); - } + + //$dc['AP'] = array(0,0,0,0,0,0,0,0); + + foreach ($nf as $nff => $cols) { + foreach ($cols as $c) { + $dc[$c] = number_format($dc[$c], $nff); } } - $where = '('.implode(' OR ', $where).')'; - - if (!sizeof($ids)) { - $this->_output(array()); - return; + } + + $this->profile('processing'); + if ($single) + $this->_output($dcs[0]); + else + $this->_output(array($pgs, $dcs)); + } + + # ------------------------------------------------------------------------ + # Check whether diffraction and snapshot images exist + function _chk_image() + { + global $jpeg_thumb_location; + if (!($this->has_arg('visit') || $this->has_arg('prop'))) + $this->_error('No visit or proposal specified'); + + $where = array(); + $ids = array(); + if ($this->has_arg('ids')) { + if (is_array($this->arg('ids'))) { + foreach ($this->arg('ids') as $i) { + array_push($ids, $i); + array_push($where, 'dc.datacollectionid=:' . sizeof($ids)); + } } - - $dct = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis, dc.datacollectionid as id, dc.startimagenumber, dc.filetemplate, dc.xtalsnapshotfullpath1 as x1, dc.xtalsnapshotfullpath2 as x2, dc.xtalsnapshotfullpath3 as x3, dc.xtalsnapshotfullpath4 as x4,dc.imageprefix as imp, dc.datacollectionnumber as run, dc.imagedirectory as dir, s.visit_number + } + $where = '(' . implode(' OR ', $where) . ')'; + + if (!sizeof($ids)) { + $this->_output(array()); + return; + } + + $dct = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis, dc.datacollectionid as id, dc.startimagenumber, dc.filetemplate, dc.xtalsnapshotfullpath1 as x1, dc.xtalsnapshotfullpath2 as x2, dc.xtalsnapshotfullpath3 as x3, dc.xtalsnapshotfullpath4 as x4,dc.imageprefix as imp, dc.datacollectionnumber as run, dc.imagedirectory as dir, s.visit_number FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $ids); - - $this->profile('dc query'); - - $dcs = array(); - foreach ($dct as $d) $dcs[$d['ID']] = $d; - - $out = array(); - foreach ($dcs as $dc) { - $debug = array(); - - $sn = 0; - $images = array(); - foreach (array('X1', 'X2', 'X3', 'X4') as $j => $im) { - array_push($images, file_exists($dc[$im]) ? 1 : 0); - if ($im == 'X1') { - $ext = pathinfo($dc[$im], PATHINFO_EXTENSION); - $thumb = str_replace('.'.$ext, 't.'.$ext, $dc[$im]); - if ($this->staff && $this->has_arg('debug')) $debug['snapshot_thumb'] = array('file' => $thumb, 'exists' => file_exists($thumb) ? 1 : 0); - if (file_exists($thumb)) $sn = 1; - } - unset($dc[$im]); - } - $dc['DIR'] = $this->ads($dc['DIR']); - $dc['X'] = $images; - - $tmp = new TemplateParser($this->db); - $di = $tmp->interpolate($jpeg_thumb_location, array('DCID' => $dc['ID'])); - - $this->profile('diffraction image'); - $die = 0; - if (file_exists($di)) $die = 1; - if ($this->staff && $this->has_arg('debug')) $debug['diffraction_thumb'] = array('file' => $di, 'exists' => file_exists($di) ? 1 : 0); - - array_push($out, array($dc['ID'], array($die,$images,$sn,$debug))); + $this->profile('dc query'); + + $dcs = array(); + foreach ($dct as $d) + $dcs[$d['ID']] = $d; + + $out = array(); + foreach ($dcs as $dc) { + $debug = array(); + + $sn = 0; + $images = array(); + foreach (array('X1', 'X2', 'X3', 'X4') as $j => $im) { + array_push($images, file_exists($dc[$im]) ? 1 : 0); + if ($im == 'X1') { + $ext = pathinfo($dc[$im], PATHINFO_EXTENSION); + $thumb = str_replace('.' . $ext, 't.' . $ext, $dc[$im]); + if ($this->staff && $this->has_arg('debug')) + $debug['snapshot_thumb'] = array('file' => $thumb, 'exists' => file_exists($thumb) ? 1 : 0); + if (file_exists($thumb)) + $sn = 1; + } + unset($dc[$im]); } - $this->_output($out); + + $dc['DIR'] = $this->ads($dc['DIR']); + $dc['X'] = $images; + + $tmp = new TemplateParser($this->db); + $di = $tmp->interpolate($jpeg_thumb_location, array('DCID' => $dc['ID'])); + + $this->profile('diffraction image'); + $die = 0; + if (file_exists($di)) + $die = 1; + if ($this->staff && $this->has_arg('debug')) + $debug['diffraction_thumb'] = array('file' => $di, 'exists' => file_exists($di) ? 1 : 0); + + array_push($out, array($dc['ID'], array($die, $images, $sn, $debug))); } - - - # ------------------------------------------------------------------------ - # Edge Scan Data - function _edge($id) { - session_write_close(); - - $info = $this->db->pq('SELECT jpegchoochfilefullpath as pth FROM energyscan WHERE energyscanid=:1', array($id)); - if (sizeof($info) == 0) { - $this->_error('No data for that collection id'); - return; - } - - $this->db->close(); - - $ch = str_replace('.png', '', $info[0]['PTH']); - - $data = array(array(), array(), array()); - if (file_exists($ch)) { - $dat = explode("\n",file_get_contents($ch)); - - foreach ($dat as $i => $d) { - if ($d) { - list($x, $y) = explode(' ', $d); - array_push($data[0], array(floatval($x), intval($y))); - } + $this->_output($out); + } + + + # ------------------------------------------------------------------------ + # Edge Scan Data + function _edge($id) + { + session_write_close(); + + $info = $this->db->pq('SELECT jpegchoochfilefullpath as pth FROM energyscan WHERE energyscanid=:1', array($id)); + if (sizeof($info) == 0) { + $this->_error('No data for that collection id'); + return; + } + + $this->db->close(); + + $ch = str_replace('.png', '', $info[0]['PTH']); + + $data = array(array(), array(), array()); + if (file_exists($ch)) { + $dat = explode("\n", file_get_contents($ch)); + + foreach ($dat as $i => $d) { + if ($d) { + list($x, $y) = explode(' ', $d); + array_push($data[0], array(floatval($x), intval($y))); } - - $dat = explode("\n",file_get_contents($ch.'.efs')); - foreach ($dat as $i => $d) { - if ($d) { - list($x, $y, $y2) = preg_split('/\s+/', trim($d)); - array_push($data[1], array(floatval($x), intval($y))); - array_push($data[2], array(floatval($x), intval($y2))); - } + } + + $dat = explode("\n", file_get_contents($ch . '.efs')); + foreach ($dat as $i => $d) { + if ($d) { + list($x, $y, $y2) = preg_split('/\s+/', trim($d)); + array_push($data[1], array(floatval($x), intval($y))); + array_push($data[2], array(floatval($x), intval($y2))); } } - - $this->_output($data); } - - - # ------------------------------------------------------------------------ - # MCA Scan Data - function _mca($id) { - session_write_close(); - - $info = $this->db->pq('SELECT filename as dir,energy,scanfilefullpath as dat + + $this->_output($data); + } + + + # ------------------------------------------------------------------------ + # MCA Scan Data + function _mca($id) + { + session_write_close(); + + $info = $this->db->pq('SELECT filename as dir,energy,scanfilefullpath as dat FROM xfefluorescencespectrum WHERE xfefluorescencespectrumid=:1', array($id)); - if (sizeof($info) == 0) { - $this->_error('No data for that spectrum id'); - return; - } - - $info = $info[0]; - $this->db->close(); - - $data = array(array(),array()); - if (file_exists($info['DAT'])) { - $dat = explode("\n",file_get_contents($info['DAT'])); - - foreach ($dat as $i => $d) { - if ($i >2 && $d) { - list($e, $v) = preg_split('/\s+/', trim($d)); - if ($i % 2 == 1) { - if (floatval($e) <= $info['ENERGY']) { - if (floatval($e) > ($info['ENERGY'] - 1100)) array_push($data[1], array(floatval($e), floatval($v))); - else array_push($data[0], array(floatval($e), floatval($v))); - } + if (sizeof($info) == 0) { + $this->_error('No data for that spectrum id'); + return; + } + + $info = $info[0]; + $this->db->close(); + + $data = array(array(), array()); + if (file_exists($info['DAT'])) { + $dat = explode("\n", file_get_contents($info['DAT'])); + + foreach ($dat as $i => $d) { + if ($i > 2 && $d) { + list($e, $v) = preg_split('/\s+/', trim($d)); + if ($i % 2 == 1) { + if (floatval($e) <= $info['ENERGY']) { + if (floatval($e) > ($info['ENERGY'] - 1100)) + array_push($data[1], array(floatval($e), floatval($v))); + else + array_push($data[0], array(floatval($e), floatval($v))); } } } - } - - - # pymca - $results = str_replace('.mca', '.results.dat', preg_replace('/(data\/\d\d\d\d\/\w\w\d+-\d+)/', '\1/processed/pymca', $info['DIR'])); - - $el_to_en = json_decode(file_get_contents('tables/energies.json'), true); - $elements = array(); - $el_no_match = array(); - $max_counts = 0; - - if (file_exists($results)) { - $dat = explode("\n",file_get_contents($results)); - foreach ($dat as $i => $d) { - if ($i < 5) { - $l = explode(' ', $d); - if ($i == 0) $max_counts = floatval($l[1]); - if (array_key_exists($l[0], $el_to_en)) { - $els = $el_to_en[$l[0]]; - if (($els[sizeof($els)-1]*1000) < ($info['ENERGY'] - 1000)) - $elements[$l[0]] = array(array_map('floatval', $els), floatval($l[1]), floatval($l[2])); - } else array_push($el_no_match, $l[0]); - } + } + + + # pymca + $results = str_replace('.mca', '.results.dat', preg_replace('/(data\/\d\d\d\d\/\w\w\d+-\d+)/', '\1/processed/pymca', $info['DIR'])); + + $el_to_en = json_decode(file_get_contents('tables/energies.json'), true); + $elements = array(); + $el_no_match = array(); + $max_counts = 0; + + if (file_exists($results)) { + $dat = explode("\n", file_get_contents($results)); + foreach ($dat as $i => $d) { + if ($i < 5) { + $l = explode(' ', $d); + if ($i == 0) + $max_counts = floatval($l[1]); + if (array_key_exists($l[0], $el_to_en)) { + $els = $el_to_en[$l[0]]; + if (($els[sizeof($els) - 1] * 1000) < ($info['ENERGY'] - 1000)) + $elements[$l[0]] = array(array_map('floatval', $els), floatval($l[1]), floatval($l[2])); + } else + array_push($el_no_match, $l[0]); } } - array_push($data, $elements); - array_push($data, $el_no_match); - array_push($data, $max_counts); - - $max = 0; - foreach ($data[0] as $d) { - if ($d[1] > $max) $max = $d[1]; - } - - array_push($data, $max); - - $this->_output($data); } - - - # ------------------------------------------------------------------------ - # Strategies for a data collection - function _dc_strategies($id) { - global $strat_align; - - $rows = $this->db->pq("SELECT s.programversion, st.rankingresolution as rankres, ssw.wedgenumber, sssw.subwedgenumber, ssw.chi, ssw.kappa, ssw.phi, dc.datacollectionid as dcid, s.comments, dc.transmission as dctrn, dc.wavelength as lam, dc.imagedirectory imd, dc.imageprefix as imp, dc.comments as dcc, dc.blsampleid as sid, sl.spacegroup as sg, sl.unitcell_a as a, sl.unitcell_b as b, sl.unitcell_c as c, sl.unitcell_alpha as al, sl.unitcell_beta as be, sl.unitcell_gamma as ga, CONCAT(CONCAT(IF(sssw.comments, sssw.comments, IF(ssw.comments, ssw.comments, s.shortcomments)), ' Wedge'), IFNULL(ssw.wedgenumber, '')) as com, sssw.axisstart as st, sssw.exposuretime as time, sssw.transmission as tran, sssw.oscillationrange as oscran, sssw.resolution as res, sssw.numberofimages as nimg, det.numberofpixelsx, det.detectorpixelsizehorizontal, sssw.rotationaxis + array_push($data, $elements); + array_push($data, $el_no_match); + array_push($data, $max_counts); + + $max = 0; + foreach ($data[0] as $d) { + if ($d[1] > $max) + $max = $d[1]; + } + + array_push($data, $max); + + $this->_output($data); + } + + + # ------------------------------------------------------------------------ + # Strategies for a data collection + function _dc_strategies($id) + { + global $strat_align; + + $rows = $this->db->pq("SELECT s.programversion, st.rankingresolution as rankres, ssw.wedgenumber, sssw.subwedgenumber, ssw.chi, ssw.kappa, ssw.phi, dc.datacollectionid as dcid, s.comments, dc.transmission as dctrn, dc.wavelength as lam, dc.imagedirectory imd, dc.imageprefix as imp, dc.comments as dcc, dc.blsampleid as sid, sl.spacegroup as sg, sl.unitcell_a as a, sl.unitcell_b as b, sl.unitcell_c as c, sl.unitcell_alpha as al, sl.unitcell_beta as be, sl.unitcell_gamma as ga, CONCAT(CONCAT(IF(sssw.comments, sssw.comments, IF(ssw.comments, ssw.comments, s.shortcomments)), ' Wedge'), IFNULL(ssw.wedgenumber, '')) as com, sssw.axisstart as st, sssw.exposuretime as time, sssw.transmission as tran, sssw.oscillationrange as oscran, sssw.resolution as res, sssw.numberofimages as nimg, det.numberofpixelsx, det.detectorpixelsizehorizontal, sssw.rotationaxis FROM screeningstrategy st INNER JOIN screeningoutput so on st.screeningoutputid = so.screeningoutputid INNER JOIN screening s on so.screeningid = s.screeningid @@ -1156,252 +1254,282 @@ function _dc_strategies($id) { LEFT OUTER JOIN detector det ON dc.detectorid = det.detectorid WHERE s.datacollectionid = :1 ORDER BY s.shortcomments, ssw.wedgenumber", array($id)); - - $output = array(); - foreach ($rows as $r) { - if (!array_key_exists($r['PROGRAMVERSION'], $output)) $output[$r['PROGRAMVERSION']] = array('CELL' => array(), 'STRATS' => array()); + + $output = array(); + foreach ($rows as $r) { + if (!array_key_exists($r['PROGRAMVERSION'], $output)) + $output[$r['PROGRAMVERSION']] = array('CELL' => array(), 'STRATS' => array()); + } + + $nf = array('A', 'B', 'C', 'AL', 'BE', 'GA'); + foreach ($rows as &$r) { + $is_align = false; + foreach ($strat_align as $sa) { + if (!$is_align) + $is_align = strpos($r['PROGRAMVERSION'], $sa) !== false; } - $nf = array('A', 'B', 'C', 'AL', 'BE', 'GA'); - foreach ($rows as &$r) { - $is_align = false; - foreach ($strat_align as $sa) { - if (!$is_align) $is_align = strpos($r['PROGRAMVERSION'], $sa) !== false; - } + if ($is_align) { + array_push($output[$r['PROGRAMVERSION']]['STRATS'], $r); + } else { - if ($is_align) { - array_push($output[$r['PROGRAMVERSION']]['STRATS'], $r); - } else { - - $t = $r['PROGRAMVERSION']; - - foreach ($r as $k => &$v) { - if (in_array($k, $nf)) { - $v = number_format(floatval($v), 2); - $output[$t]['CELL'][$k] = $v; - unset($r[$k]); - } + $t = $r['PROGRAMVERSION']; - if ($k == 'TRAN') $v = number_format($v, 1); - if ($k == 'TIME') $v = number_format($v, 3); - if ($k == 'OSCRAN') $v = number_format($v, 2); - if ($k == 'RES') $v = number_format($v, 2); - if ($k == 'RANKRES') $v = number_format($v, 2); - } - - $output[$t]['CELL']['SG'] = $r['SG']; - unset($r['SG']); - - $r['COM'] = str_replace('EDNA', '', $r['COM']); - $r['COM'] = str_replace('Mosflm ', '', $r['COM']); - - $r['VPATH'] = join('/', array_slice(explode('/', $r['IMD']),0,6)); - list(,,$r['BL']) = explode('/', $r['IMD']); - - # we dont actually use this in the display view maybe Deprecate? - # detector diameter in mm - if ($r['DETECTORPIXELSIZEHORIZONTAL'] && $r['NUMBEROFPIXELSX']) { - $diam = $r['DETECTORPIXELSIZEHORIZONTAL']*$r['NUMBEROFPIXELSX']*1000; - } else { - // drop back to pilatus 6m diameter - $diam = 415; + foreach ($r as $k => &$v) { + if (in_array($k, $nf)) { + $v = number_format(floatval($v), 2); + $output[$t]['CELL'][$k] = $v; + unset($r[$k]); } - $r['DIST'] = $this->_r_to_dist($diam, $r['LAM'], $r['RES']); - $r['ATRAN'] = round($r['TRAN']/100.0*$r['DCTRN'],1); - list($r['NTRAN'], $r['NEXP']) = $this->_norm_et($r['ATRAN'], $r['TIME']); - $r['AP'] = $this->_get_ap($r['DCC']); - - array_push($output[$t]['STRATS'], $r); + if ($k == 'TRAN') + $v = number_format($v, 1); + if ($k == 'TIME') + $v = number_format($v, 3); + if ($k == 'OSCRAN') + $v = number_format($v, 2); + if ($k == 'RES') + $v = number_format($v, 2); + if ($k == 'RANKRES') + $v = number_format($v, 2); } - } - - $this->_output(array(sizeof($rows), $output)); - } - - # ------------------------------------------------------------------------ - # Normalise transmission fo 25hz data collection - function _norm_et($t, $e) { - if ($t < 100 && $e > 0.04) { - $f = $e / 0.04; - $maxe = 0.04; - $maxt = ($e / 0.04) * $t; - - if ($maxt > 100) { - $maxe *= $maxt/100; - $maxt = 100; + + $output[$t]['CELL']['SG'] = $r['SG']; + unset($r['SG']); + + $r['COM'] = str_replace('EDNA', '', $r['COM']); + $r['COM'] = str_replace('Mosflm ', '', $r['COM']); + + $r['VPATH'] = join('/', array_slice(explode('/', $r['IMD']), 0, 6)); + list(,, $r['BL']) = explode('/', $r['IMD']); + + # we dont actually use this in the display view maybe Deprecate? + # detector diameter in mm + if ($r['DETECTORPIXELSIZEHORIZONTAL'] && $r['NUMBEROFPIXELSX']) { + $diam = $r['DETECTORPIXELSIZEHORIZONTAL'] * $r['NUMBEROFPIXELSX'] * 1000; + } else { + // drop back to pilatus 6m diameter + $diam = 415; } - return array(number_format($maxt,1), number_format($maxe,3)); - } else { - return array($t, $e); + + $r['DIST'] = $this->_r_to_dist($diam, $r['LAM'], $r['RES']); + $r['ATRAN'] = round($r['TRAN'] / 100.0 * $r['DCTRN'], 1); + list($r['NTRAN'], $r['NEXP']) = $this->_norm_et($r['ATRAN'], $r['TIME']); + $r['AP'] = $this->_get_ap($r['DCC']); + + array_push($output[$t]['STRATS'], $r); } - } - - # ------------------------------------------------------------------------ - # Convert resolution to detector distance - function _r_to_dist($diam, $lambda, $r) { - $result = 0; - try { - $b=$lambda/(2*$r); - $d=2*asin($b); - $f=2*tan($d); - $result = number_format($diam/$f, 2); - } catch (\Exception $e) { - error_log('Error converting resolution to distance, lambda=' . $lambda . ' r=' . $r . ' Exception: ' . $e->getMessage()); + + $this->_output(array(sizeof($rows), $output)); + } + + # ------------------------------------------------------------------------ + # Normalise transmission fo 25hz data collection + function _norm_et($t, $e) + { + if ($t < 100 && $e > 0.04) { + $f = $e / 0.04; + $maxe = 0.04; + $maxt = ($e / 0.04) * $t; + + if ($maxt > 100) { + $maxe *= $maxt / 100; + $maxt = 100; } - return $result; + return array(number_format($maxt, 1), number_format($maxe, 3)); + } else { + return array($t, $e); } - - # ------------------------------------------------------------------------ - # Work out which aperture is selected - function _get_ap($com) { - $aps = array('Aperture: Large'=>'LARGE_APERTURE', - 'Aperture: Medium'=>'MEDIUM_APERTURE', - 'Aperture: Small'=>'SMALL_APERTURE', - 'Aperture: 10'=>'In_10', - 'Aperture: 20'=>'In_20', - 'Aperture: 30'=>'In_30', - 'Aperture: 50'=>'In_50', - 'Aperture: 70'=>'In_70'); - - $app = ''; - foreach ($aps as $k => $v) { - if (strpos($com, $k) !== False) $app = $v; - } - - return $app; + } + + # ------------------------------------------------------------------------ + # Convert resolution to detector distance + function _r_to_dist($diam, $lambda, $r) + { + $result = 0; + try { + $b = $lambda / (2 * $r); + $d = 2 * asin($b); + $f = 2 * tan($d); + $result = number_format($diam / $f, 2); + } catch (\Exception $e) { + error_log('Error converting resolution to distance, lambda=' . $lambda . ' r=' . $r . ' Exception: ' . $e->getMessage()); + } + return $result; + } + + # ------------------------------------------------------------------------ + # Work out which aperture is selected + function _get_ap($com) + { + $aps = array( + 'Aperture: Large' => 'LARGE_APERTURE', + 'Aperture: Medium' => 'MEDIUM_APERTURE', + 'Aperture: Small' => 'SMALL_APERTURE', + 'Aperture: 10' => 'In_10', + 'Aperture: 20' => 'In_20', + 'Aperture: 30' => 'In_30', + 'Aperture: 50' => 'In_50', + 'Aperture: 70' => 'In_70' + ); + + $app = ''; + foreach ($aps as $k => $v) { + if (strpos($com, $k) !== False) + $app = $v; } - - - # ------------------------------------------------------------------------ - # Image quality indicators from distl - function _image_qi($id) { - session_write_close(); - $iqs = array(array(), array(), array(), array(), array()); - - #$this->db->set_debug(True); - - $where = $id; - $args = array(); - /* - $dcg = $this->db->pq("SELECT datacollectiongroupid as dcg FROM datacollection WHERE datacollectionid=:1", array($id)); - if (!sizeof($dcg)) $this->error('No such data collection'); - - $dcids = $this->db->pq("SELECT datacollectionid as id FROM datacollection WHERE datacollectiongroupid=:1", array($dcg[0]['DCG'])); - - $where = array(); - $args = array(); - foreach ($dcids as $i => $id) { - //array_push($where, ':'.($i+1)); - array_push($where, $id['ID']); - //array_push($args, $id['ID']); - } - $where = implode($where, ', '); - */ - - /*$tot = $this->db->pq("SELECT count(im.imagenumber) as tot - FROM image im - INNER JOIN imagequalityindicators imq ON imq.imageid = im.imageid AND (im.datacollectionid IN ($where)) - ORDER BY imagenumber", $args); - - $int = intval($tot[0]['TOT'] / 250); - array_push($args, $int);*/ - #im.datacollectionid=:1 - $imqs = $this->db->pq("SELECT imq.imagenumber as nim, imq.method2res as res, imq.spottotal as s, imq.totalintegratedsignal, imq.goodbraggcandidates as b, imq.dozor_score as d + return $app; + } + + + # ------------------------------------------------------------------------ + # Image quality indicators from distl + function _image_qi($id) + { + session_write_close(); + $iqs = array(array(), array(), array(), array(), array()); + + #$this->db->set_debug(True); + + $where = $id; + $args = array(); + /* + $dcg = $this->db->pq("SELECT datacollectiongroupid as dcg FROM datacollection WHERE datacollectionid=:1", array($id)); + if (!sizeof($dcg)) $this->error('No such data collection'); + $dcids = $this->db->pq("SELECT datacollectionid as id FROM datacollection WHERE datacollectiongroupid=:1", array($dcg[0]['DCG'])); + $where = array(); + $args = array(); + foreach ($dcids as $i => $id) { + //array_push($where, ':'.($i+1)); + array_push($where, $id['ID']); + //array_push($args, $id['ID']); + } + $where = implode($where, ', '); + */ + + /*$tot = $this->db->pq("SELECT count(im.imagenumber) as tot + FROM image im + INNER JOIN imagequalityindicators imq ON imq.imageid = im.imageid AND (im.datacollectionid IN ($where)) + ORDER BY imagenumber", $args); + $int = intval($tot[0]['TOT'] / 250); + array_push($args, $int);*/ + + #im.datacollectionid=:1 + $imqs = $this->db->pq("SELECT imq.imagenumber as nim, imq.method2res as res, imq.spottotal as s, imq.totalintegratedsignal, imq.goodbraggcandidates as b, imq.dozor_score as d FROM imagequalityindicators imq WHERE imq.datacollectionid IN ($where) ORDER BY imq.imagenumber", $args); - foreach ($imqs as $imq) { - array_push($iqs[0], array(intval($imq['NIM']), $this->_null_or($imq['S'], 'int'))); - array_push($iqs[1], array(intval($imq['NIM']), $this->_null_or($imq['B'], 'int'))); - array_push($iqs[2], array(intval($imq['NIM']), $this->_null_or($imq['RES'], 'float'))); - array_push($iqs[3], array(intval($imq['NIM']), $this->_null_or($imq['TOTALINTEGRATEDSIGNAL'], 'float'))); - array_push($iqs[4], array(intval($imq['NIM']), $this->_null_or($imq['D'], 'float'))); - } - - $this->_output($iqs); + foreach ($imqs as $imq) { + array_push($iqs[0], array(intval($imq['NIM']), $this->_null_or($imq['S'], 'int'))); + array_push($iqs[1], array(intval($imq['NIM']), $this->_null_or($imq['B'], 'int'))); + array_push($iqs[2], array(intval($imq['NIM']), $this->_null_or($imq['RES'], 'float'))); + array_push($iqs[3], array(intval($imq['NIM']), $this->_null_or($imq['TOTALINTEGRATEDSIGNAL'], 'float'))); + array_push($iqs[4], array(intval($imq['NIM']), $this->_null_or($imq['D'], 'float'))); } - function _null_or($field, $conversion) { - $val = $field; - if ($conversion == 'float') $val = floatval($field); - if ($conversion == 'int') $val = intval($field); - - return $field == null ? $field : $val; - } - - # ------------------------------------------------------------------------ - # Grid Scan Info - function _grid_info() { - $info = $this->db->pq("SELECT dc.datacollectiongroupid, dc.datacollectionid, dc.axisstart, p.posx as x, p.posy as y, p.posz as z, g.dx_mm, g.dy_mm, g.steps_x, g.steps_y, g.pixelspermicronx, g.pixelspermicrony, g.snapshot_offsetxpixel, g.snapshot_offsetypixel, g.orientation, g.snaked + $this->_output($iqs); + } + + function _null_or($field, $conversion) + { + $val = $field; + if ($conversion == 'float') + $val = floatval($field); + if ($conversion == 'int') + $val = intval($field); + + return $field == null ? $field : $val; + } + + # ------------------------------------------------------------------------ + # Grid Scan Info + function _grid_info() + { + $info = $this->db->pq("SELECT dc.datacollectiongroupid, dc.datacollectionid, dc.axisstart, p.posx as x, p.posy as y, p.posz as z, g.dx_mm, g.dy_mm, g.steps_x, g.steps_y, IFNULL(g.micronsperpixelx,g.pixelspermicronx) as micronsperpixelx, IFNULL(g.micronsperpixely,g.pixelspermicrony) as micronsperpixely, g.snapshot_offsetxpixel, g.snapshot_offsetypixel, g.orientation, g.snaked, DATE_FORMAT(dc.starttime, '%Y%m%d') as startdate, xrc.status as xrcstatus, xrcr.xraycentringresultid FROM gridinfo g INNER JOIN datacollection dc on (dc.datacollectionid = g.datacollectionid) or (dc.datacollectiongroupid = g.datacollectiongroupid) LEFT OUTER JOIN position p ON dc.positionid = p.positionid + LEFT OUTER JOIN xraycentring xrc ON dc.datacollectiongroupid = xrc.datacollectiongroupid + LEFT OUTER JOIN xraycentringresult xrcr ON xrc.xraycentringid = xrcr.xraycentringid WHERE dc.datacollectionid = :1 ", array($this->arg('id'))); - if (!sizeof($info)) $this->_output(array()); - else { - foreach ($info[0] as $k => &$v) { - if ($k == 'ORIENTATION') continue; - $v = floatval($v); - } - $this->_output($info[0]); + if (!sizeof($info)) + $this->_output(array()); + else { + foreach ($info[0] as $k => &$v) { + if ($k == 'ORIENTATION' || $k == 'XRCSTATUS') + continue; + $v = floatval($v); } + $this->_output($info[0]); } + } - function _grid_map() { - if (!$this->has_arg('id')) $this->_error('No datacollection id specified'); + function _grid_map() + { + if (!$this->has_arg('id')) + $this->_error('No datacollection id specified'); - $map = $this->db->pq("SELECT positionx, positiony, imagenumber, outputfileid + $map = $this->db->pq("SELECT positionx, positiony, imagenumber, outputfileid FROM gridimagemap WHERE datacollectionid=:1", array($this->arg('id'))); - $this->_output($map); - } + $this->_output($map); + } - # XRC - function _grid_xrc() { - $info = $this->db->pq("SELECT dc.datacollectiongroupid, dc.datacollectionid, xrc.method, xrc.x, xrc.y - FROM gridinfo g - INNER JOIN datacollection dc ON dc.datacollectiongroupid = g.datacollectiongroupid - INNER JOIN xraycentringresult xrc ON xrc.gridinfoid = g.gridinfoid + # XRC + function _grid_xrc() + { + $info = $this->db->pq("SELECT dc.datacollectiongroupid, dc.datacollectionid, + xrc.xraycentringtype as method, xrcr.xraycentringresultid, + xrcr.centreofmassx as x, xrcr.centreofmassy as y, xrcr.centreofmassz as z + FROM datacollection dc + INNER JOIN xraycentring xrc ON xrc.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN xraycentringresult xrcr ON xrcr.xraycentringid = xrc.xraycentringid WHERE dc.datacollectionid = :1 ", array($this->arg('id'))); - if (!sizeof($info)) $this->_output(array()); - else { - foreach ($info[0] as $k => &$v) { - if ($k == 'METHOD') continue; - $v = floatval($v); - } - $this->_output($info[0]); + if (!sizeof($info)) + $this->_output(array('total' => 0, 'data' => array())); + else { + foreach ($info as &$i) { + foreach ($i as $k => &$v) { + if ($k == 'METHOD') + continue; + $v = round(floatval($v), 2); + } } + $this->_output(array('total' => sizeof($info), 'data' => $info)); } + } - # ------------------------------------------------------------------------ - # Fluorescence Map Info - function _fluo_map() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - $where = 'ses.proposalid=:1'; - $args = array($this->proposalid); + # ------------------------------------------------------------------------ + # Fluorescence Map Info + function _fluo_map() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - if ($this->has_arg('id')) { - $where .= ' AND g.datacollectionid=:'.(sizeof($args)+1); - array_push($args, $this->arg('id')); - } + $where = 'ses.proposalid=:1'; + $args = array($this->proposalid); - if ($this->has_arg('sid')) { - $where .= ' AND s.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } + if ($this->has_arg('id')) { + $where .= ' AND g.datacollectionid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('id')); + } - $maps = $this->db->pq("SELECT xfm.xrffluorescencemappingid, IF(xfroi.scalar IS NOT NULL, xfroi.scalar, CONCAT(xfroi.element, '-', xfroi.edge)) as title, xfm.data, xfm.opacity, xfm.min, xfm.max, xfroi.element, xfroi.scalar, xfroi.edge, xfroi.startenergy, xfroi.endenergy, dc.blsubsampleid, dc.blsampleid, dc.datacollectionid, g.steps_x, g.steps_y, g.snaked, g.orientation + if ($this->has_arg('sid')) { + $where .= ' AND s.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + } + + $maps = $this->db->pq("SELECT xfm.xrffluorescencemappingid, IF(xfroi.scalar IS NOT NULL, xfroi.scalar, CONCAT(xfroi.element, '-', xfroi.edge)) as title, xfm.data, xfm.opacity, xfm.min, xfm.max, xfroi.element, xfroi.scalar, xfroi.edge, xfroi.startenergy, xfroi.endenergy, dc.blsubsampleid, dc.blsampleid, dc.datacollectionid, g.steps_x, g.steps_y, g.snaked, g.orientation FROM xrffluorescencemapping xfm INNER JOIN gridinfo g ON g.gridinfoid = xfm.gridinfoid INNER JOIN datacollection dc ON dc.datacollectionid = g.datacollectionid @@ -1412,45 +1540,52 @@ function _fluo_map() { WHERE $where", $args); - foreach ($maps as &$m) { - $m["DATA"] = json_decode(gzdecode($m["DATA"])); - } - - $this->_output($maps); + foreach ($maps as &$m) { + $m["DATA"] = json_decode(gzdecode($m["DATA"])); } - - # ------------------------------------------------------------------------ - # Update comment for a data collection - function _set_comment() { - if (!$this->has_arg('t')) $this->_error('No data type specified'); - if (!$this->arg('id')) $this->_error('No data collection id specified'); - - $types = array('data' => array('datacollection', 'datacollectionid'), - 'edge' => array('energyscan', 'energyscanid'), - 'mca' => array('xfefluorescencespectrum', 'xfefluorescencespectrumid'), - ); + $this->_output($maps); + } - $types['grid'] = $types['data']; - - if (!array_key_exists($this->arg('t'), $types)) $this->_error('No such data type'); - $t = $types[$this->arg('t')]; - - $com = $this->db->pq('SELECT comments from '.$t[0].' WHERE '.$t[1].'=:1', array($this->arg('id'))); - - if (!sizeof($com)) $this->_error('No such data collection'); - - foreach (array('COMMENTS') as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE $t[0] set $f=:1 where $t[1]=:2", array($this->arg($f), $this->arg('id'))); - } + + # ------------------------------------------------------------------------ + # Update comment for a data collection + function _set_comment() + { + if (!$this->has_arg('t')) + $this->_error('No data type specified'); + if (!$this->arg('id')) + $this->_error('No data collection id specified'); + + $types = array( + 'data' => array('datacollection', 'datacollectionid'), + 'edge' => array('energyscan', 'energyscanid'), + 'mca' => array('xfefluorescencespectrum', 'xfefluorescencespectrumid'), + ); + + $types['grid'] = $types['data']; + + if (!array_key_exists($this->arg('t'), $types)) + $this->_error('No such data type'); + $t = $types[$this->arg('t')]; + + $com = $this->db->pq('SELECT comments from ' . $t[0] . ' WHERE ' . $t[1] . '=:1', array($this->arg('id'))); + + if (!sizeof($com)) + $this->_error('No such data collection'); + + foreach (array('COMMENTS') as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE $t[0] set $f=:1 where $t[1]=:2", array($this->arg($f), $this->arg('id'))); } } - - - # Plot R_d for fast_dp - function _rd($aid, $id) { - $info = $this->db->pq("SELECT appa.filename,appa.filepath,appa.filetype + } + + + # Plot R_d for fast_dp + function _rd($aid, $id) + { + $info = $this->db->pq("SELECT appa.filename,appa.filepath,appa.filetype FROM autoprocintegration api INNER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid INNER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid @@ -1458,104 +1593,116 @@ function _rd($aid, $id) { INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid WHERE api.datacollectionid = :1 AND api.autoprocprogramid=:2 AND appa.filetype LIKE 'Log'", array($id, $aid)); - - if (!sizeof($info)) $this->_error('The specified auto processing doesnt exist'); - else $info = $info[0]; - $this->db->close(); - - $file = $info['FILEPATH'].'/xdsstat.log'; - - $rows = array(); - if (file_exists($file)) { - $log = file_get_contents($file); - - $start = 0; - foreach (explode("\n", $log) as $l) { - if (strpos(trim($l), 'Framediff #refs R_d n-notfriedel Rd-notfriedel n-friedel Rd-friedel dummy $$') !== False) { - $start = 1; - } - - if ($start) $start++; - - if (strpos(trim($l), '$$') !== False && $start > 4) { - $start = 0; - } - - if ($start > 3) { - $start++; - if (trim($l)) { - $f = preg_split('/\s+/', trim($l)); - array_push($rows, array(intval($f[0]), floatval($f[2]))); - } + + if (!sizeof($info)) + $this->_error('The specified auto processing doesnt exist'); + else + $info = $info[0]; + $this->db->close(); + + $file = $info['FILEPATH'] . '/xdsstat.log'; + + $rows = array(); + if (file_exists($file)) { + $log = file_get_contents($file); + } elseif (file_exists($file.'.gz')) { + $log = gzdecode(file_get_contents($file.'.gz')); + } + if (isset($log)) { + + $start = 0; + foreach (explode("\n", $log) as $l) { + if (strpos(trim($l), 'Framediff #refs R_d n-notfriedel Rd-notfriedel n-friedel Rd-friedel dummy $$') !== False) { + $start = 1; + } + + if ($start) + $start++; + + if (strpos(trim($l), '$$') !== False && $start > 4) { + $start = 0; + } + + if ($start > 3) { + $start++; + if (trim($l)) { + $f = preg_split('/\s+/', trim($l)); + array_push($rows, array(intval($f[0]), floatval($f[2]))); } - } } - - $this->_output($rows); - } - - - - function _get_symmetry() { - if (!($this->has_arg('a') && $this->has_arg('b') && $this->has_arg('c') && $this->has_arg('al') && $this->has_arg('be') && $this->has_arg('ga') && $this->has_arg('sg'))) $this->_error('Missing parameters'); - - exec('./scripts/symtry.sh '.$this->arg('a').' '.$this->arg('b').' '.$this->arg('c').' '.$this->arg('al').' '.$this->arg('be').' '.$this->arg('ga').' '.$this->arg('sg'), $ret); - - $matrices = array(); - foreach ($ret as $l) { - $parts = array_map('floatval', explode(' ', $l)); - array_push($matrices, array(array_slice($parts, 0, 4), - array_slice($parts, 4, 4), - array_slice($parts, 8, 4)) - ); - } - - $this->_output($matrices); + + $this->_output($rows); + } + + + + function _get_symmetry() + { + if (!($this->has_arg('a') && $this->has_arg('b') && $this->has_arg('c') && $this->has_arg('al') && $this->has_arg('be') && $this->has_arg('ga') && $this->has_arg('sg'))) + $this->_error('Missing parameters'); + + exec('./scripts/symtry.sh ' . $this->arg('a') . ' ' . $this->arg('b') . ' ' . $this->arg('c') . ' ' . $this->arg('al') . ' ' . $this->arg('be') . ' ' . $this->arg('ga') . ' ' . $this->arg('sg'), $ret); + + $matrices = array(); + foreach ($ret as $l) { + $parts = array_map('floatval', explode(' ', $l)); + array_push( + $matrices, + array( + array_slice($parts, 0, 4), + array_slice($parts, 4, 4), + array_slice($parts, 8, 4) + ) + ); } + $this->_output($matrices); + } - function _get_comments() { - $where = '1=1'; - $args = array(); - // $this->db->set_debug(True); - if ($this->has_arg('dcid')) { - $where .= ' AND dcc.datacollectioncommentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('dcid')); - } + function _get_comments() + { + $where = '1=1'; + $args = array(); + // $this->db->set_debug(True); - if ($this->has_arg('id')) { - $where .= ' AND dc.datacollectionid=:'.(sizeof($args)+1); - array_push($args, $this->arg('id')); - } + if ($this->has_arg('dcid')) { + $where .= ' AND dcc.datacollectioncommentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('dcid')); + } - $tot = $this->db->pq("SELECT count(dcc.datacollectioncommentid) as tot + if ($this->has_arg('id')) { + $where .= ' AND dc.datacollectionid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('id')); + } + + $tot = $this->db->pq("SELECT count(dcc.datacollectioncommentid) as tot FROM datacollectioncomment dcc INNER JOIN datacollection dc ON dc.datacollectionid = dcc.datacollectionid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - array_push($args, $start); - array_push($args, $end); + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); - $comments = $this->db->paginate("SELECT dcc.datacollectioncommentid, dcc.datacollectionid, dcc.personid, dcc.comments, TO_CHAR(dcc.createtime, 'DD-MM-YYYY HH24:MI:SS') as createtime, TO_CHAR(dcc.modtime, 'DD-MM-YYYY HH24:MI:SS') as modtime, p.givenname, p.familyname + + $comments = $this->db->paginate("SELECT dcc.datacollectioncommentid, dcc.datacollectionid, dcc.personid, dcc.comments, TO_CHAR(dcc.createtime, 'DD-MM-YYYY HH24:MI:SS') as createtime, TO_CHAR(dcc.modtime, 'DD-MM-YYYY HH24:MI:SS') as modtime, p.givenname, p.familyname FROM datacollectioncomment dcc INNER JOIN datacollection dc ON dc.datacollectionid = dcc.datacollectionid INNER JOIN person p ON p.personid = dcc.personid @@ -1563,67 +1710,79 @@ function _get_comments() { ORDER BY dcc.createtime ASC", $args); - if ($this->has_arg('dcid')) { - if (sizeof($comments)) $this->_output($comments[0]); - else $this->_error('No such comment'); - } else $this->_output(array('total' => $tot, 'data' => $comments)); - } - - - function _add_comment() { - if (!$this->has_arg('DATACOLLECTIONID')) $this->_error('No datacollection specified'); - if (!$this->has_arg('PERSONID')) $this->_error('No person specified'); - if (!$this->has_arg('COMMENTS')) $this->_error('No comment specified'); - - $this->db->pq("INSERT INTO datacollectioncomment (datacollectioncommentid, datacollectionid, personid, comments, createtime) - VALUES (s_datacollectioncomment.nextval, :1, :2, :3, CURRENT_TIMESTAMP) RETURNING datacollectioncommentid INTO :id", - array($this->arg('DATACOLLECTIONID'), $this->arg('PERSONID'), $this->arg('COMMENTS'))); - - $this->_output(array('DATACOLLECTIONCOMMENTID' => $this->db->id(), - 'GIVENNAME' => $this->user->givenname, - 'FAMILYNAME' => $this->user->familyname, - 'CREATETIME' => date('d-m-Y H:i:s') - )); + if ($this->has_arg('dcid')) { + if (sizeof($comments)) + $this->_output($comments[0]); + else + $this->_error('No such comment'); + } else + $this->_output(array('total' => $tot, 'data' => $comments)); + } + + + function _add_comment() + { + if (!$this->has_arg('DATACOLLECTIONID')) + $this->_error('No datacollection specified'); + if (!$this->has_arg('PERSONID')) + $this->_error('No person specified'); + if (!$this->has_arg('COMMENTS')) + $this->_error('No comment specified'); + + $this->db->pq( + "INSERT INTO datacollectioncomment (datacollectioncommentid, datacollectionid, personid, comments, createtime) + VALUES (s_datacollectioncomment.nextval, :1, :2, :3, CURRENT_TIMESTAMP) RETURNING datacollectioncommentid INTO :id", + array($this->arg('DATACOLLECTIONID'), $this->arg('PERSONID'), $this->arg('COMMENTS')) + ); + + $this->_output(array( + 'DATACOLLECTIONCOMMENTID' => $this->db->id(), + 'GIVENNAME' => $this->user->givenName, + 'FAMILYNAME' => $this->user->familyName, + 'CREATETIME' => date('d-m-Y H:i:s') + )); + } + + + + + # ------------------------------------------------------------------------ + # Dat Plot + function _plot() + { + session_write_close(); + if (!$this->has_arg('id')) { + $this->_error('No data collection id specified'); + return; } - - - - # ------------------------------------------------------------------------ - # Dat Plot - function _plot() { - session_write_close(); - if (!$this->has_arg('id')) { - $this->_error('No data collection id specified'); - return; - } - - $info = $this->db->pq('SELECT ses.visit_number, dc.datfullpath, dc.datacollectionnumber as scan, dc.imageprefix as imp, dc.imagedirectory as dir + $info = $this->db->pq('SELECT ses.visit_number, dc.datfullpath, dc.datacollectionnumber as scan, dc.imageprefix as imp, dc.imagedirectory as dir FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession ses ON dcg.sessionid = ses.sessionid WHERE datacollectionid=:1', array($this->arg('id'))); - if (sizeof($info) == 0) { - $this->_error('No data for that collection id'); - return; - } else $info = $info[0]; - - $info['VISIT'] = $this->arg('prop') .'-'.$info['VISIT_NUMBER']; - - $pth = $info['DATFULLPATH'] ? $info['DATFULLPATH'] : str_replace($info['VISIT'], $info['VISIT'].'/.ispyb', $this->ads($info['DIR']).$info['IMP'].'/'.$info['SCAN'].'.dat'); - - $data = array(); - if (file_exists($pth) && is_readable(($pth))) { - $dat = explode("\n",file_get_contents($pth)); - - foreach ($dat as $i => $d) { - if ($d) { - list($x, $y) = preg_split('/\s+/', trim($d)); - array_push($data, array(floatval($x), floatval($y))); - } + if (sizeof($info) == 0) { + $this->_error('No data for that collection id'); + return; + } else + $info = $info[0]; + + $info['VISIT'] = $this->arg('prop') . '-' . $info['VISIT_NUMBER']; + + $pth = $info['DATFULLPATH'] ? $info['DATFULLPATH'] : str_replace($info['VISIT'], $info['VISIT'] . '/.ispyb', $this->ads($info['DIR']) . $info['IMP'] . '/' . $info['SCAN'] . '.dat'); + + $data = array(); + if (file_exists($pth) && is_readable(($pth))) { + $dat = explode("\n", file_get_contents($pth)); + + foreach ($dat as $i => $d) { + if ($d) { + list($x, $y) = preg_split('/\s+/', trim($d)); + array_push($data, array(floatval($x), floatval($y))); } } - - $this->_output(array($data)); } + + $this->_output(array($data)); + } } diff --git a/api/src/Page/Download.php b/api/src/Page/Download.php index 1366101a0..1b5823b7a 100644 --- a/api/src/Page/Download.php +++ b/api/src/Page/Download.php @@ -5,107 +5,115 @@ use SynchWeb\Page; use SynchWeb\TemplateParser; -use Symfony\Component\Filesystem\Exception\IOExceptionInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\Response; +use ZipStream\Option\Archive; +use ZipStream\ZipStream; + +ini_set('max_execution_time', 0); // To allow large file downloads class Download extends Page { - public static $arg_list = array('id' => '\d+', - 'aid' => '\d+', - 'run' => '\d+', - 'visit' => '\w+\d+-\d+', - 'u' => '\w+\d+', - 's' => '\d', - 'log' => '\d', - 'archive' => '\d', - 'LogFiles' => '([\w|\.])+', - 'ty' => '\w+', - 'pdb' => '\d', - 'map' => '\d', - 'validity' => '.*', - - 'ppl' => '\w+', - 'aid' => '\w+', - - 'filetype' => '\w+', - 'blsampleid' => '\d+', - 'dcg' => '\d+', - - 'download' => '\d', - 'AUTOPROCPROGRAMID' => '\d+', - 'AUTOPROCPROGRAMATTACHMENTID' => '\d+', - ); - - - public static $dispatch = array( - array('/plots', 'get', '_auto_processing_plots'), - array('/csv/visit/:visit', 'get', '_csv_report'), - array('/sign', 'post', '_sign_url'), - array('/data/visit/:visit', 'get', '_download_visit'), - array('/attachments', 'get', '_get_attachments'), - array('/attachment/id/:id/aid/:aid', 'get', '_get_attachment'), - array('/dc/id/:id', 'get', '_download'), - - array('/ap/attachments(/:AUTOPROCPROGRAMATTACHMENTID)(/dl/:download)', 'get', '_get_autoproc_attachments'), - array('/ap/archive/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_archive'), - ); - - - # ------------------------------------------------------------------------ - # Generate a one time token for access to downloads - function _sign_url() { - if (!$this->has_arg('validity')) $this->_error('No validity specified'); - $token = md5(uniqid()); - - $this->db->pq("INSERT INTO SW_onceToken (token, validity, proposalid, personid) VALUES (:1, :2, :3, :4)", array($token, $this->arg('validity'), $this->proposalid, $this->user->personid)); - $this->_output(array('token' => $token)); - } - - - # ------------------------------------------------------------------------ - # SAXS Specific Visit Download Link - function _download_visit() { - $filesystem = new Filesystem(); - $tp = new TemplateParser($this->db); - $visit = $tp->visit_dir(array('VISIT' => $this->arg('visit'))); - - $data = $visit.'/.ispyb/download/download.zip'; - - if ($filesystem->exists($data)) { - $response = new BinaryFileResponse($data); - $response->headers->set("Content-Type", "application/octet-stream"); - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $this->arg('visit').'_download.zip' - ); - $response->send(); - } else $this->_error('There doesnt seem to be a data archive available for this visit'); + public static $arg_list = array( + 'id' => '\d+', + 'run' => '\d+', + 'visit' => '\w+\d+-\d+', + 'u' => '\w+\d+', + 's' => '\d', + 'log' => '\d', + 'archive' => '\d', + 'LogFiles' => '([\w|\.])+', + 'ty' => '\w+', + 'pdb' => '\d', + 'map' => '\d', + 'validity' => '.*', + + 'ppl' => '\w+', + 'aid' => '\w+', + + 'filetype' => '\w+', + 'blsampleid' => '\d+', + 'dcg' => '\d+', + + 'download' => '\d', + 'AUTOPROCPROGRAMID' => '\d+', + 'AUTOPROCPROGRAMATTACHMENTID' => '\d+', + ); + + + public static $dispatch = array( + array('/plots', 'get', '_auto_processing_plots'), + array('/csv/visit/:visit', 'get', '_csv_report'), + array('/sign', 'post', '_sign_url'), + array('/data/visit/:visit', 'get', '_download_visit'), + array('/attachments', 'get', '_get_attachments'), + array('/attachment/id/:id/aid/:aid', 'get', '_get_attachment'), + array('/dc/id/:id', 'get', '_download'), + + array('/ap/attachments(/:AUTOPROCPROGRAMATTACHMENTID)(/dl/:download)', 'get', '_get_autoproc_attachments'), + array('/ap/archive/:AUTOPROCPROGRAMID', 'get', '_get_autoproc_archive'), + ); + + + # ------------------------------------------------------------------------ + # Generate a one time token for access to downloads + function _sign_url() + { + if (!$this->has_arg('validity')) + $this->_error('No validity specified'); + $token = md5(uniqid()); + + $this->db->pq("INSERT INTO SW_onceToken (token, validity, proposalid, personid) VALUES (:1, :2, :3, :4)", array($token, $this->arg('validity'), $this->proposalid, $this->user->personId)); + $this->_output(array('token' => $token)); + } + + + # ------------------------------------------------------------------------ + # SAXS Specific Visit Download Link + function _download_visit() + { + $filesystem = new Filesystem(); + $tp = new TemplateParser($this->db); + $visit = $tp->visit_dir(array('VISIT' => $this->arg('visit'))); + + $data = $visit . '/.ispyb/download/download.zip'; + + if ($filesystem->exists($data)) { + $response = new BinaryFileResponse($data); + $response->headers->set("Content-Type", "application/octet-stream"); + $this->_set_disposition_attachment($response, $this->arg('visit') . '_download.zip'); + $response->send(); + } else + $this->_error('There doesnt seem to be a data archive available for this visit'); + } + + # ------------------------------------------------------------------------ + # Download mtz/log file for Fast DP / XIA2 + # TODO: Delete me + # This method either returns a list of plots from MX auto processing tools (n_obs, n_uniq, completeness etc.) + # Or returns a specific plot based on auto processing attachment id (aid). + # Individual plotly format Graphs can be returned via an aid, but will not be included in the list of plots (as their format is different) + function _auto_processing_plots() + { + global $ap_types; + if (!$this->has_arg('id')) + $this->_error('No data collection', 'No data collection id specified'); + + $args = array($this->arg('id')); + $where = ''; + + // Are we asking for a specific plot attachment? + // Example Plotly plots are not included in the general processing results plot types + if ($this->has_arg('aid')) { + $where .= ' AND appa.autoprocprogramattachmentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('aid')); } - # ------------------------------------------------------------------------ - # Download mtz/log file for Fast DP / XIA2 - # TODO: Delete me - # This method either returns a list of plots from MX auto processing tools (n_obs, n_uniq, completeness etc.) - # Or returns a specific plot based on auto processing attachment id (aid). - # Individual plotly format Graphs can be returned via an aid, but will not be included in the list of plots (as their format is different) - function _auto_processing_plots() { - global $ap_types; - if (!$this->has_arg('id')) $this->_error('No data collection', 'No data collection id specified'); - - $args = array($this->arg('id')); - $where = ''; - - // Are we asking for a specific plot attachment? - // Example Plotly plots are not included in the general processing results plot types - if ($this->has_arg('aid')) { - $where .= ' AND appa.autoprocprogramattachmentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('aid')); - } - - $rows = $this->db->pq("SELECT appa.filename, + $rows = $this->db->pq("SELECT appa.filename, appa.filepath, api.autoprocprogramid, appa.autoprocprogramattachmentid, @@ -115,81 +123,88 @@ function _auto_processing_plots() { INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid WHERE appa.filetype='Graph' AND api.datacollectionid = :1 $where", $args); - foreach ($rows as $k => &$r) { - foreach ($ap_types as $id => $name) { - if (strpos($r['TYPE'], $id)) { - $r['TYPE'] = $name; - break; - } + foreach ($rows as $k => &$r) { + foreach ($ap_types as $id => $name) { + if (strpos($r['TYPE'], $id)) { + $r['TYPE'] = $name; + break; } + } + + $json = $r['FILEPATH'] . '/' . $r['FILENAME']; + $r['PLOTS'] = array(); + if (file_exists($json)) { + $cont = file_get_contents($json); + } elseif (file_exists($json.'.gz')) { + $cont = gzdecode(file_get_contents($json.'.gz')); + } - $json = $r['FILEPATH'].'/'.$r['FILENAME']; - $r['PLOTS'] = array(); - if (file_exists($json)) { - $cont = file_get_contents($json); - - $plotData = json_decode($cont); - $r['PLOTS'] = $plotData; - - // We need to check if this is a plotly type (in which case this should be a request for a specific attachment) - // A plotly chart must define two keys 'data' and 'layout' - $r['PLOTLY'] = false; - - if (array_key_exists('data', $plotData) && array_key_exists('layout', $plotData)) { - // This is a plotly chart - // If we are looking for a specific plot (by attachment id) then OK... - if($this->has_arg('aid')){ - $r['PLOTLY'] = true; - } else { - // ..if not, we should remove this from the list of returned plots - // Autoprocessing results use the same bespoke format for charts - // and we don't want to include plotly in those aggregated results - unset($rows[$k]); - continue; - } + if (isset($cont)) { + + $plotData = json_decode($cont); + $r['PLOTS'] = $plotData; + + // We need to check if this is a plotly type (in which case this should be a request for a specific attachment) + // A plotly chart must define two keys 'data' and 'layout' + $r['PLOTLY'] = false; + + if (array_key_exists('data', $plotData) && array_key_exists('layout', $plotData)) { + // This is a plotly chart + // If we are looking for a specific plot (by attachment id) then OK... + if ($this->has_arg('aid')) { + $r['PLOTLY'] = true; + } else { + // ..if not, we should remove this from the list of returned plots + // Autoprocessing results use the same bespoke format for charts + // and we don't want to include plotly in those aggregated results + unset($rows[$k]); + continue; } } - - unset($r['FILENAME']); - unset($r['FILEPATH']); } - // Because we may have removed plotly type plots from the list, we should re-index to provide a consistent array. - // Also ensures there will be an index at 0 for the first item. - $results = array_values($rows); - $this->_output($results); - } - - # ------------------------------------------------------------------------ - # Return list of attachments for an autoproc run - function __get_autoproc_attachments() { - if (!$this->has_arg('prop')) $this->_error('No proposal specific', 'No proposal specified'); - - $args = array($this->proposalid); - $where = ''; - - if ($this->has_arg(('FILENAME'))) { - $where .= " AND appa.filename LIKE CONCAT('%',:".(sizeof($args)+1)."), '%')"; - array_push($args, $this->arg('FILENAME')); - } + unset($r['FILENAME']); + unset($r['FILEPATH']); + } + // Because we may have removed plotly type plots from the list, we should re-index to provide a consistent array. + // Also ensures there will be an index at 0 for the first item. + $results = array_values($rows); + $this->_output($results); + } + + + # ------------------------------------------------------------------------ + # Return list of attachments for an autoproc run + function __get_autoproc_attachments() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specific', 'No proposal specified'); + + $args = array($this->proposalid); + $where = ''; + + if ($this->has_arg(('FILENAME'))) { + $where .= " AND appa.filename LIKE CONCAT('%',:" . (sizeof($args) + 1) . "), '%')"; + array_push($args, $this->arg('FILENAME')); + } - if ($this->has_arg('FILETYPE')) { - $where .= ' AND appa.filetype =:'.(sizeof($args)+1); - array_push($args, $this->arg('FILETYPE')); - } + if ($this->has_arg('FILETYPE')) { + $where .= ' AND appa.filetype =:' . (sizeof($args) + 1); + array_push($args, $this->arg('FILETYPE')); + } - if ($this->has_arg('AUTOPROCPROGRAMID')) { - $where .= ' AND app.autoprocprogramid=:'.(sizeof($args)+1); - array_push($args, $this->arg('AUTOPROCPROGRAMID')); - } + if ($this->has_arg('AUTOPROCPROGRAMID')) { + $where .= ' AND app.autoprocprogramid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('AUTOPROCPROGRAMID')); + } - if ($this->has_arg('AUTOPROCPROGRAMATTACHMENTID')) { - $where .= ' AND appa.autoprocprogramattachmentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('AUTOPROCPROGRAMATTACHMENTID')); - } + if ($this->has_arg('AUTOPROCPROGRAMATTACHMENTID')) { + $where .= ' AND appa.autoprocprogramattachmentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('AUTOPROCPROGRAMATTACHMENTID')); + } - $rows = $this->db->union(array( - "SELECT app.autoprocprogramid, appa.filename, appa.filepath, appa.filetype, appa.autoprocprogramattachmentid, dc.datacollectionid, appa.importancerank + $rows = $this->db->union(array( + "SELECT app.autoprocprogramid, appa.filename, appa.filepath, appa.filetype, appa.autoprocprogramattachmentid, dc.datacollectionid, appa.importancerank FROM autoprocintegration api INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid @@ -197,7 +212,7 @@ function __get_autoproc_attachments() { INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 $where", - "SELECT app.autoprocprogramid, appa.filename, appa.filepath, appa.filetype, appa.autoprocprogramattachmentid, dc.datacollectionid, appa.importancerank + "SELECT app.autoprocprogramid, appa.filename, appa.filepath, appa.filetype, appa.autoprocprogramattachmentid, dc.datacollectionid, appa.importancerank FROM autoprocprogram app INNER JOIN processingjob pj on pj.processingjobid = app.processingjobid INNER JOIN autoprocprogramattachment appa ON appa.autoprocprogramid = app.autoprocprogramid @@ -206,76 +221,85 @@ function __get_autoproc_attachments() { INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 $where ORDER BY importancerank" - ), $args); - - return $rows; - } - - - function _get_autoproc_attachments() { - $rows = $this->__get_autoproc_attachments(); - if ($this->has_arg('AUTOPROCPROGRAMATTACHMENTID')) { - if (!sizeof($rows)) $this->_error('No such attachment'); - else { - if ($this->has_arg('download')) { - $this->_get_file($rows[0]['AUTOPROCPROGRAMID'], $rows[0]); - } else $this->_output($rows[0]); - } - } else $this->_output($rows); - } - - - /** - * Download a file to the browser - * This function is used to download autoproc and phasing run attachments. - * It sets a maximum amount of memory for the download. - * The $id is used as a prefix to the filename. - * - * @param integer $id One of AutoProcProgramId or PhasingProgramRunId - * @param array $file Array that must include FILENAME (including extension) and FILEPATH - */ - function _get_file($id, $file) { - // We don't want to allow unlimited file sizes - ini_set('memory_limit', '512M'); - $filesystem = new Filesystem(); - - $filename = $file['FILEPATH'].'/'.$file['FILENAME']; - - // Do the check first, if no file quit early - if ($filesystem->exists($filename)) { - - $response = new BinaryFileResponse($filename); - - # Set mime / content type + ), $args); + + return $rows; + } + + + function _get_autoproc_attachments() + { + $rows = $this->__get_autoproc_attachments(); + if ($this->has_arg('AUTOPROCPROGRAMATTACHMENTID')) { + if (!sizeof($rows)) + $this->_error('No such attachment'); + else { + if ($this->has_arg('download')) { + $this->_get_file($rows[0]['AUTOPROCPROGRAMID'], $rows[0]); + } else + $this->_output($rows[0]); + } + } else + $this->_output($rows); + } + + + /** + * Download a file to the browser + * This function is used to download autoproc and phasing run attachments. + * It sets a maximum amount of memory for the download. + * The $id is used as a prefix to the filename. + * + * @param integer $id One of AutoProcProgramId or PhasingProgramRunId + * @param array $file Array that must include FILENAME (including extension) and FILEPATH + */ + function _get_file($id, $file) + { + // We don't want to allow unlimited file sizes + ini_set('memory_limit', '512M'); + $filesystem = new Filesystem(); + + $filename = $file['FILEPATH'] . '/' . $file['FILENAME']; + + // Do the check first, if no file quit early + if ($filesystem->exists($filename)) { + $response = new BinaryFileResponse($filename); + $this->set_mime_content($response, $filename, $id); + $response->headers->set("Content-Length", filesize($filename)); + } elseif ($filesystem->exists($filename.'.gz')) { + $filename = $filename.'.gz'; + if ($this->arg('download') == 1) { + // View log file, so unzip and serve + $response = new Response(readgzfile($filename)); $this->set_mime_content($response, $file['FILENAME'], $id); - - // All OK - send it - // We were getting out of memory errors - switch off output buffer to fix - if (ob_get_level()) { - ob_end_clean(); - } - // Setting content length means browser can indicate how long is left - $response->headers->set("Content-Length", filesize($filename)); - - $response->send(); - exit(); } else { - $this->_error("No such file, the specified file " . $filename . " doesn't exist"); + // Download gzipped file + $response = new BinaryFileResponse($filename); + $this->set_mime_content($response, $filename, $id); + $response->headers->set("Content-Length", filesize($filename)); } + } else { + $this->_error("No such file, the specified file " . $filename . " doesn't exist"); } + $response->send(); + } - # ------------------------------------------------------------------------ - # CSV Report of Data Collections - function _csv_report() { - if (!$this->has_arg('visit')) $this->_error('No visit specified', 'You must specify a visit to download a report for'); + # ------------------------------------------------------------------------ + # CSV Report of Data Collections + function _csv_report() + { + if (!$this->has_arg('visit')) + $this->_error('No visit specified', 'You must specify a visit to download a report for'); - $vis = $this->db->pq("SELECT s.sessionid,s.beamlinename,TO_CHAR(s.startdate, 'DD_MM_YYYY') as st FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1", array($this->arg('visit'))); + $vis = $this->db->pq("SELECT s.sessionid,s.beamlinename,TO_CHAR(s.startdate, 'DD_MM_YYYY') as st FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1", array($this->arg('visit'))); - if (!sizeof($vis)) $this->_error('No such visit', 'The specified visit doesnt exist'); - else $vis = $vis[0]; + if (!sizeof($vis)) + $this->_error('No such visit', 'The specified visit doesnt exist'); + else + $vis = $vis[0]; - $rows = $this->db->pq("SELECT dc.imageprefix,s.beamlinename,dc.datacollectionnumber,TO_CHAR(dc.starttime, 'DD/MM/YYYY HH24:MI:SS'), sa.name, p.name as protein, dc.numberofimages, dc.wavelength, dc.detectordistance, dc.exposuretime, dc.axisstart, dc.axisrange, dc.xbeam, dc.ybeam, dc.resolution, dc.comments + $rows = $this->db->pq("SELECT dc.imageprefix,s.beamlinename,dc.datacollectionnumber,TO_CHAR(dc.starttime, 'DD/MM/YYYY HH24:MI:SS'), sa.name, p.name as protein, dc.numberofimages, dc.wavelength, dc.detectordistance, dc.exposuretime, dc.axisstart, dc.axisrange, dc.xbeam, dc.ybeam, dc.resolution, dc.comments FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid @@ -284,51 +308,54 @@ function _csv_report() { LEFT OUTER JOIN protein p ON c.proteinid = p.proteinid WHERE dcg.sessionid=:1 ORDER BY dc.starttime", array($vis['SESSIONID'])); - $this->app->response->headers->set("Content-type", "application/vnd.ms-excel"); - $this->app->response->headers->set("Content-disposition", "attachment; filename=".$vis['ST']."_".$vis['BEAMLINENAME']."_".$this->arg('visit').".csv"); - print "Image prefix,Beamline,Run no,Start Time,Sample Name,Protein Acronym,# images, Wavelength (angstrom), Distance (mm), Exp. Time (sec), Phi start (deg), Phi range (deg), Xbeam (mm), Ybeam (mm), Detector resol. (angstrom), Comments\n"; - foreach ($rows as $r) { - $r['COMMENTS'] = '"'.$r['COMMENTS'].'"'; - print implode(',', array_values($r))."\n"; - } + $this->app->response->headers->set("Content-type", "application/vnd.ms-excel"); + $this->_set_disposition_attachment($this->app->response, $vis['ST'] . "_" . $vis['BEAMLINENAME'] . "_" . $this->arg('visit') . ".csv"); + print "Image prefix,Beamline,Run no,Start Time,Sample Name,Protein Acronym,# images, Wavelength (angstrom), Distance (mm), Exp. Time (sec), Phi start (deg), Phi range (deg), Xbeam (mm), Ybeam (mm), Detector resol. (angstrom), Comments\n"; + foreach ($rows as $r) { + $r['COMMENTS'] = '"' . $r['COMMENTS'] . '"'; + print implode(',', array_values($r)) . "\n"; } + } - # ------------------------------------------------------------------------ - # Get dc attachmmnts - function _get_attachments() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('id') && !$this->has_arg('blsampleid') && !$this->has_arg('dcg')) $this->_error('No data collection or sample specified'); + # ------------------------------------------------------------------------ + # Get dc attachmmnts + function _get_attachments() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('id') && !$this->has_arg('blsampleid') && !$this->has_arg('dcg')) + $this->_error('No data collection or sample specified'); - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; - if ($this->has_arg('id')) { - $where .= ' AND dca.datacollectionid=:'.(sizeof($args)+1); - array_push($args, $this->arg('id')); - } + if ($this->has_arg('id')) { + $where .= ' AND dca.datacollectionid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('id')); + } - if ($this->has_arg('dcg')) { - $where .= ' AND dc.datacollectiongroupid=:'.(sizeof($args)+1); - array_push($args, $this->arg('dcg')); - } + if ($this->has_arg('dcg')) { + $where .= ' AND dc.datacollectiongroupid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('dcg')); + } - if ($this->has_arg('aid')) { - $where .= ' AND dca.datacollectionfileattachmentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('aid')); - } + if ($this->has_arg('aid')) { + $where .= ' AND dca.datacollectionfileattachmentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('aid')); + } - if ($this->has_arg('filetype')) { - $where .= ' AND dca.filetype LIKE :'.(sizeof($args)+1); - array_push($args, $this->arg('filetype')); - } + if ($this->has_arg('filetype')) { + $where .= ' AND dca.filetype LIKE :' . (sizeof($args) + 1); + array_push($args, $this->arg('filetype')); + } - if ($this->has_arg('blsampleid')) { - $where .= ' AND dc.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('blsampleid')); - } + if ($this->has_arg('blsampleid')) { + $where .= ' AND dc.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('blsampleid')); + } - $rows = $this->db->pq("SELECT dca.filefullpath, dca.filetype, dca.datacollectionfileattachmentid, dca.datacollectionid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, dc.blsampleid, dc.blsubsampleid, g.dx_mm, g.dy_mm, g.steps_x, g.steps_y, g.orientation, g.snaked + $rows = $this->db->pq("SELECT dca.filefullpath, dca.filetype, dca.datacollectionfileattachmentid, dca.datacollectionid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, dc.blsampleid, dc.blsubsampleid, g.dx_mm, g.dy_mm, g.steps_x, g.steps_y, g.orientation, g.snaked FROM datacollectionfileattachment dca INNER JOIN datacollection dc ON dc.datacollectionid = dca.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid @@ -337,197 +364,214 @@ function _get_attachments() { LEFT OUTER JOIN gridinfo g ON g.datacollectiongroupid = dc.datacollectiongroupid WHERE $where", $args); - foreach($rows as &$r) { - $r['FILENAME'] = basename($r['FILEFULLPATH']); - $info = pathinfo($r['FILENAME']); - $r['NAME'] = basename($r['FILENAME'],'.'.$info['extension']); + foreach ($rows as &$r) { + $r['FILENAME'] = basename($r['FILEFULLPATH']); + $info = pathinfo($r['FILENAME']); + $r['NAME'] = basename($r['FILENAME'], '.' . $info['extension']); - $r['FILEFULLPATH'] = preg_replace('/.*\/'.$r['VISIT'].'\//', '', $r['FILEFULLPATH']); + $r['FILEFULLPATH'] = preg_replace('/.*\/' . $r['VISIT'] . '\//', '', $r['FILEFULLPATH']); - foreach (array('DX_MM', 'DY_MM', 'STEPS_X', 'STEPS_Y') as $k) { - $r[$k] = floatval($r[$k]); - } + foreach (array('DX_MM', 'DY_MM', 'STEPS_X', 'STEPS_Y') as $k) { + $r[$k] = floatval($r[$k]); } + } - if ($this->has_arg('aid')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such attachment'); - - } else $this->_output($rows); - } + if ($this->has_arg('aid')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such attachment'); + } else + $this->_output($rows); + } - function _get_attachment() { - $filesystem = new Filesystem(); - $rows = $this->db->pq("SELECT filefullpath + function _get_attachment() + { + $filesystem = new Filesystem(); + $rows = $this->db->pq("SELECT filefullpath FROM datacollectionfileattachment WHERE datacollectionid=:1 AND datacollectionfileattachmentid=:2", array($this->arg('id'), $this->arg('aid'))); - if (!sizeof($rows)) $this->_error('No such attachment'); + if (!sizeof($rows)) + $this->_error('No such attachment'); - $filename = $rows[0]['FILEFULLPATH']; + $filename = $rows[0]['FILEFULLPATH']; - if ($filesystem->exists($filename)) { - $response = new BinaryFileResponse($filename); + if ($filesystem->exists($filename)) { + $response = new BinaryFileResponse($filename); - $this->set_mime_content($response, $filename); - $response->headers->set("Content-Length", filesize($filename)); - $response->send(); - } else { - error_log("Download file " . $filename . " not found"); - $this->_error('Attachment not found on filesystem', 404); - } + $this->set_mime_content($response, $filename); + $response->headers->set("Content-Length", filesize($filename)); + $response->send(); + } else { + error_log("Download file " . $filename . " not found"); + $this->_error('Attachment not found on filesystem', 404); } + } - # ------------------------------------------------------------------------ - # Get an archive of an autoproc - function _get_autoproc_archive() { - if (!$this->has_arg('prop')) { - $this->_error('No proposal specific', 'No proposal specified'); - } + # ------------------------------------------------------------------------ + # Get an archive of an autoproc + function _get_autoproc_archive() + { - $aps = $this->db->union( - array( - "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus + if (!$this->has_arg('prop')) { + $this->_error('No proposal specific', 'No proposal specified'); + } + + $aps = $this->db->union( + array( + "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus FROM autoprocintegration api INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid INNER JOIN datacollection dc ON dc.datacollectionid = api.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 AND app.autoprocprogramid=:2", - "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus + "SELECT app.autoprocprogramid, app.processingprograms, app.processingstatus FROM autoprocprogram app INNER JOIN processingjob pj on pj.processingjobid = app.processingjobid INNER JOIN datacollection dc ON dc.datacollectionid = pj.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE s.proposalid=:1 AND app.autoprocprogramid=:2", - ), - array($this->proposalid, $this->arg('AUTOPROCPROGRAMID')) - ); - - if (!sizeof($aps)) { - return $this->_error('No such auto processing'); - } - $ap = $aps[0]; + ), + array($this->proposalid, $this->arg('AUTOPROCPROGRAMID')) + ); - $files = $this->__get_autoproc_attachments(); - if (!sizeof($files)) { - return $this->_error('No files to archive'); - } + if (!sizeof($aps)) { + return $this->_error('No such auto processing'); + } + $ap = $aps[0]; - $clean_program = preg_replace('/[^A-Za-z0-9\-]/', '', $ap['PROCESSINGPROGRAMS']); - $tar = '/tmp/' . $this->arg('AUTOPROCPROGRAMID') . '_' . $clean_program . '.tar'; - $gz = $tar . '.gz'; + $files = $this->__get_autoproc_attachments(); + if (!sizeof($files)) { + return $this->_error('No files to archive'); + } - $a = new \PharData($tar); + $clean_program = preg_replace('/[^A-Za-z0-9\-]/', '', $ap['PROCESSINGPROGRAMS']); + $zipName = $this->arg('AUTOPROCPROGRAMID') . '_' . $clean_program; + $this->_streamZipFile($files, $zipName); + } + + /** + * Stream a zip file based on files on the file system + * @param files array of files to send + * @param zipName name of the zip that is sent + */ + function _streamZipFile($files, $zipName) + { + $response = new StreamedResponse(function () use ($files, $zipName) + { + # enable output of HTTP headers + $options = new Archive(); + $options->setSendHttpHeaders(true); + $options->setContentType('application/octet-stream'); + $options->setFlushOutput(true); + $options->setZeroHeader(true); + $options->setEnableZip64(true); + + # create a new zipstream object + $zip = new ZipStream($zipName . '.zip', $options); foreach ($files as $file) { $filename = $file['FILEPATH'] . '/' . $file['FILENAME']; - $a->addFile($filename, basename($filename)); + if (file_exists($filename)) { + $zip->addFileFromPath(basename($filename), $filename); + } elseif (file_exists($filename.'.gz')) { + $zip->addFileFromPath(basename($filename).'.gz', $filename.'.gz'); + } } - $a->compress(\Phar::GZ); - unlink($tar); - $response = new BinaryFileResponse($gz); + $zip->finish(); + }); + $response->send(); + } + + /** + * Set mime and content type headers for the provided response. + * Determines the mime type from the filename extension. + * + * @param BinaryFileResponse $response Symfony Response Object + * @param string $filename Filename to be downloaded (will use basename in case file path is provided) + * @param string $prefix Add this string to the filename "prefix_filename" + * @return void + */ + function set_mime_content($response, $filename, $prefix = null) + { + $path_ext = pathinfo($filename, PATHINFO_EXTENSION); + // If we are downloading the file (not inline) then set a sensible name + $saved_filename = $prefix ? implode('_', array($prefix, basename($filename))) : basename($filename); + + if (in_array($path_ext, array('html', 'htm'))) { + $response->headers->set("Content-Type", "text/html"); + $this->_set_disposition_inline($response); + } elseif ($path_ext == 'pdf') { + $response->headers->set("Content-Type", "application/pdf"); + $this->_set_disposition_attachment($response, $saved_filename); + } elseif ($path_ext == 'png') { + $response->headers->set("Content-Type", "image/png"); + $this->_set_disposition_attachment($response, $saved_filename); + } elseif (in_array($path_ext, array('jpg', 'jpeg'))) { + $response->headers->set("Content-Type", "image/jpeg"); + $this->_set_disposition_attachment($response, $saved_filename); + } elseif (in_array($path_ext, array('log', 'txt', 'error', 'LP', 'json', 'lsa'))) { + $response->headers->set("Content-Type", "text/plain"); + $this->_set_disposition_inline($response); + } else { $response->headers->set("Content-Type", "application/octet-stream"); - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $this->arg('AUTOPROCPROGRAMID') . '_' . $clean_program . '.tar.gz' - ); - $response->deleteFileAfterSend(true); - $response->send(); - exit(); - + $this->_set_disposition_attachment($response, $saved_filename); } - - - # ------------------------------------------------------------------------ - # Set mime and content type for a file - /** - * Set mime and content type headers for the provided response. - * Determines the mime type from the filename extension. - * - * @param BinaryFileResponse $response Symfony Response Object - * @param string $filename Filename to be downloaded (will use basename in case file path is provided) - * @param string $prefix Add this string to the filename "prefix_filename" - * @return void - */ - function set_mime_content($response, $filename, $prefix=null) { - $path_ext = pathinfo($filename, PATHINFO_EXTENSION); - // If we are downloading the file (not inline) then set a sensible name - $saved_filename = $prefix ? implode('_', array($prefix, basename($filename))) : basename($filename); - - if (in_array($path_ext, array('html', 'htm'))) { - $response->headers->set("Content-Type", "text/html"); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE); - } elseif ($path_ext == 'pdf') { - $response->headers->set("Content-Type", "application/pdf"); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $saved_filename); - } elseif ($path_ext == 'png') { - $response->headers->set("Content-Type", "image/png"); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $saved_filename); - } elseif (in_array($path_ext, array('jpg', 'jpeg'))) { - $response->headers->set("Content-Type", "image/jpeg"); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $saved_filename); - } elseif (in_array($path_ext, array('log', 'txt', 'error', 'LP', 'json'))) { - $response->headers->set("Content-Type", "text/plain"); - $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE); - } else { - $response->headers->set("Content-Type", "application/octet-stream"); - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $saved_filename - ); - } + } + + function _set_disposition_attachment($response, $filename) { + $response->headers->set("Content-Disposition", + (new ResponseHeaderBag())->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename) + ); + } + + function _set_disposition_inline($response) { + $response->headers->set("Content-Disposition", + (new ResponseHeaderBag())->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, '') + ); + } + + + # ------------------------------------------------------------------------ + # Download Data + function _download() + { + $filesystem = new Filesystem(); + session_write_close(); + if (!$this->has_arg('id')) { + $this->_error('No data collection id specified'); + return; } - # ------------------------------------------------------------------------ - # Download Data - function _download() { - $filesystem = new Filesystem(); - session_write_close(); - if (!$this->has_arg('id')) { - $this->_error('No data collection id specified'); - return; - } - - $info = $this->db->pq('SELECT s.visit_number, dc.datacollectionnumber as scan, dc.imageprefix as imp, dc.imagedirectory as dir + $info = $this->db->pq('SELECT s.visit_number, dc.datacollectionnumber as scan, dc.imageprefix as imp, dc.imagedirectory as dir FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid WHERE dc.datacollectionid=:1', array($this->arg('id'))); - if (sizeof($info) == 0) { - $this->_error('No data for that collection id'); - return; - } else $info = $info[0]; - - $info['VISIT'] = $this->arg('prop') .'-'.$info['VISIT_NUMBER']; - - $data = str_replace($info['VISIT'], $info['VISIT'].'/.ispyb', $this->ads($info['DIR']).$info['IMP'].'/download/download.zip'); - - if ($filesystem->exists($data)) { - $response = new BinaryFileResponse($data); - $response->headers->set("Content-Type", "application/octet-stream"); - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $this->arg('id').'_download.zip' - ); - $response->send(); - } else { - error_log("Download file " . $data . " not found"); - $this->_error('File not found on filesystem', 404); - } - } + if (sizeof($info) == 0) { + $this->_error('No data for that collection id'); + return; + } else + $info = $info[0]; + + $info['VISIT'] = $this->arg('prop') . '-' . $info['VISIT_NUMBER']; + $data = str_replace($info['VISIT'], $info['VISIT'] . '/.ispyb', $this->ads($info['DIR']) . $info['IMP'] . '/download/download.zip'); - # ------------------------------------------------------------------------ - # Force browser to download file - # Deprecated function now we are using symfony filesystem - function _header($f) { - header("Content-Type: application/octet-stream"); - header("Content-Transfer-Encoding: Binary"); - header("Content-disposition: attachment; filename=\"$f\""); + if ($filesystem->exists($data)) { + $response = new BinaryFileResponse($data); + $response->headers->set("Content-Type", "application/octet-stream"); + $this->_set_disposition_attachment($response, $this->arg('id') . '_download.zip'); + $response->send(); + } else { + error_log("Download file " . $data . " not found"); + $this->_error('File not found on filesystem', 404); } + } } diff --git a/api/src/Page/EM/Config.php b/api/src/Page/EM/Config.php index 9f6a09d67..1ba699063 100644 --- a/api/src/Page/EM/Config.php +++ b/api/src/Page/EM/Config.php @@ -11,7 +11,7 @@ trait Config */ private function configExitIfNoMicroscopes() { - if (count($this->_get_beamlines_from_type('em')) == 0) { + if (count($this->_get_beamlines_from_type('em', false)) == 0) { $message = 'Electron microscopes are not specified'; error_log($message); $this->_error($message, 500); diff --git a/api/src/Page/EM/DataCollection.php b/api/src/Page/EM/DataCollection.php index 29c241fc2..de777a903 100644 --- a/api/src/Page/EM/DataCollection.php +++ b/api/src/Page/EM/DataCollection.php @@ -94,7 +94,7 @@ public function dataCollectionCreate() $dataCollectionId = $this->db->id(); $this->db->end_transaction(); - } catch (Exception $e) { + } catch (\Exception $e) { error_log("Failed to add DataCollection to database."); $this->_error("Failed to add DataCollection to database.", 500); } diff --git a/api/src/Page/EM/Relion.php b/api/src/Page/EM/Relion.php index f65d4d0c8..c6a33a774 100644 --- a/api/src/Page/EM/Relion.php +++ b/api/src/Page/EM/Relion.php @@ -236,7 +236,7 @@ private function relionAddJob($dataCollectionId, $workflowParameters) } $this->db->end_transaction(); - } catch (Exception $exception) { + } catch (\Exception $exception) { $message = 'Failed to add ProcessingJob to database.'; error_log($message); error_log($exception->getMessage()); diff --git a/api/src/Page/EM/Scipion.php b/api/src/Page/EM/Scipion.php index eaf895e64..0fdad4f86 100644 --- a/api/src/Page/EM/Scipion.php +++ b/api/src/Page/EM/Scipion.php @@ -57,7 +57,7 @@ public function scipionStart() // Read workflow template file try { $template_json_string = file_get_contents("{$template_path}/{$template_file}"); - } catch (Exception $e) { + } catch (\Exception $e) { error_log("Failed to read workflow template: {$template_path}/{$template_file}"); $this->_error("Failed to read workflow template for electron microscopy “{$session['BEAMLINENAME']}”.", 500); } @@ -114,7 +114,7 @@ public function scipionStart() try { file_put_contents("{$workflow_path}/{$workflow_file}", $workflow_json_string); - } catch (Exception $e) { + } catch (\Exception $e) { error_log("Failed to write workflow file: {$workflow_path}/{$workflow_file}"); $this->_error('Failed to write workflow file.', 500); } diff --git a/api/src/Page/Exp.php b/api/src/Page/Exp.php index 3f942462e..88cc5f9bb 100644 --- a/api/src/Page/Exp.php +++ b/api/src/Page/Exp.php @@ -6,120 +6,118 @@ class Exp extends Page { - - public static $arg_list = array( - - // Filters - 'CONTAINERID' => '\d+', - 'BLSAMPLEID' => '\d+', - - - // Replacement for DiffractionPlan in various tables - 'DATACOLLECTIONPLANID' => '\d+', - - - // DiffractionPlan (=>DataCollectionPlan) - 'DIFFRACTIONPLANID' => '\d+', - 'BLSAMPLEID' => '\d+', - 'EXPERIMENTKIND' => '\w+', - 'EXPOSURETIME' => '\d+(.\d+)?', - 'REQUIREDRESOLUTION' => '\d+(.\d+)?', - 'PREFERREDBEAMSIZEX' => '\d+', - 'PREFERREDBEAMSIZEY' => '\d+', - 'BOXSIZEX' => '\d+', - 'BOXSIZEY' => '\d+', - 'NUMBEROFIMAGES' => '\d+', - 'AXISSTART' => '-?\d+(.\d+)?', - 'AXISRANGE' => '\d+(.\d+)?', - 'TRANSMISSION' => '\d+(.\d+)?', - 'ENERGY' => '\d+(.\d+)?', - 'MONOCHROMATOR' => '\w+', - 'MONOBANDWIDTH' => '\d+(.\d+)?', - 'COMMENTS' => '.*', - 'PLANORDER' => '\d+', - - - // Detector - 'DETECTORID' => '\d+', - 'DETECTORTYPE' => '[\w-\s]+', - 'DETECTORMANUFACTURER' => '[\w-]+', - 'DETECTORMODEL' => '[\w-\s]+', - 'DETECTORPIXELSIZEHORIZONTAL' => '\d+', - 'DETECTORPIXELSIZEVERTICAL' => '\d+', - 'DETECTORDISTANCEMIN' => '\d+', - 'DETECTORDISTANCEMAX' => '\d+', - 'DENSITY' => '\d+(.\d+)?', - 'COMPOSITION' => '\w+', - 'DETECTORMAXRESOLUTION' => '\d+(.\d+)?', - 'DETECTORMINRESOLUTION' => '\d+(.\d+)?', - 'DETECTORROLLMIN' => '-?\d+(.\d+)?', - 'DETECTORROLLMAX' => '-?\d+(.\d+)?', - 'SENSORTHICKNESS' => '\d+', - 'DETECTORSERIALNUMBER' => '[\w-]+', - 'NUMBEROFPIXELSX' => '\d+', - 'NUMBEROFPIXELSY' => '\d+', - - - // Scan Param Service - 'SCANPARAMETERSSERVICEID' => '\d+', - 'NAME' => '[\w|\s|-]+', - 'DESCRIPTION' => '.*', - - - // Scan Param Model - 'SCANPARAMETERSMODELID' => '\d+', - 'SEQUENCENUMBER' => '\d+', - 'START' => '\d+(.\d+)?', - 'STOP' => '\d+(.\d+)?', - 'STEP' => '\d+(.\d+)?', - 'ARRAY' => '[\d+(.\d+)?)\s,]+', - - - // DCPlan has Detector - 'DATACOLLECTIONPLANHASDETECTORID' => '\d+', - 'EXPOSURETIME' => '\d+(.\d+)?', - 'DISTANCE' => '\d+(.\d+)?', - 'ROLL' => '\d+(.\d+)?', - - - // Beamline Setup - 'BEAMLINESETUPID' => '\d+', - 'BEAMLINENAME' => '[\w-]+', - 'BEAMSIZEXMAX' => '\d+(.\d+)?', - 'BEAMSIZEXMIN' => '\d+(.\d+)?', - 'BEAMSIZEYMAX' => '\d+', - 'BEAMSIZEYMIN' => '\d+', - 'BOXSIZEXMAX' => '\d+(.\d+)?', - 'BOXSIZEXMIN' => '\d+(.\d+)?', - 'BOXSIZEYMAX' => '\d+(.\d+)?', - 'BOXSIZEYMIN' => '\d+(.\d+)?', - 'CS' => '\d+(.\d+)?', - 'ENERGYMAX' => '\d+', - 'ENERGYMIN' => '\d+', - 'GONIOSTATMAXOSCILLATIONWIDTH' => '\d+(.\d+)?', - 'GONIOSTATMINOSCILLATIONWIDTH' => '\d+(.\d+)?', - 'KAPPAMAX' => '-?\d+(.\d+)?', - 'KAPPAMIN' => '-?\d+(.\d+)?', - 'MAXEXPOSURETIMEPERIMAGE' => '\d+(.\d+)?', - 'MINEXPOSURETIMEPERIMAGE' => '\d+(.\d+)?', - 'MAXTRANSMISSION' => '\d+', - 'MINTRANSMISSION' => '\d+', - 'NUMBEROFIMAGESMAX' => '\d+', - 'NUMBEROFIMAGESMIN' => '\d+', - 'OMEGAMAX' => '-?\d+(.\d+)?', - 'OMEGAMIN' => '-?\d+(.\d+)?', - 'PHIMAX' => '-?\d+(.\d+)?', - 'PHIMIN' => '-?\d+(.\d+)?', - 'MONOBANDWIDTHMIN' => '\d+(.\d+)?', - 'MONOBANDWIDTHMAX' => '\d+(.\d+)?', - 'ACTIVE' => '\d', - - 'EXPERIMENTTYPEID' => '\d+', - ); - - - public static $dispatch = array( + public static $arg_list = array( + + // Shared parameters + 'EXPOSURETIME' => '\d+(.\d+)?', //DiffractionPlan and DCPlan has Detector + 'BLSAMPLEID' => '\d+', // Filters and DiffractionPlan (=>DataCollectionPlan) + + // Filters + 'CONTAINERID' => '\d+', + + // Replacement for DiffractionPlan in various tables + 'DATACOLLECTIONPLANID' => '\d+', + + // DiffractionPlan (=>DataCollectionPlan) + 'DIFFRACTIONPLANID' => '\d+', + 'EXPERIMENTKIND' => '\w+', + 'REQUIREDRESOLUTION' => '\d+(.\d+)?', + 'PREFERREDBEAMSIZEX' => '\d+', + 'PREFERREDBEAMSIZEY' => '\d+', + 'BOXSIZEX' => '\d+', + 'BOXSIZEY' => '\d+', + 'NUMBEROFIMAGES' => '\d+', + 'AXISSTART' => '-?\d+(.\d+)?', + 'AXISRANGE' => '\d+(.\d+)?', + 'TRANSMISSION' => '\d+(.\d+)?', + 'ENERGY' => '\d+(.\d+)?', + 'MONOCHROMATOR' => '\w+', + 'MONOBANDWIDTH' => '\d+(.\d+)?', + 'COMMENTS' => '.*', + 'PLANORDER' => '\d+', + + + // Detector + 'DETECTORID' => '\d+', + 'DETECTORTYPE' => '[\w\-\s]+', + 'DETECTORMANUFACTURER' => '[\w\-]+', + 'DETECTORMODEL' => '[\w\-\s]+', + 'DETECTORPIXELSIZEHORIZONTAL' => '\d+', + 'DETECTORPIXELSIZEVERTICAL' => '\d+', + 'DETECTORDISTANCEMIN' => '\d+', + 'DETECTORDISTANCEMAX' => '\d+', + 'DENSITY' => '\d+(.\d+)?', + 'COMPOSITION' => '\w+', + 'DETECTORMAXRESOLUTION' => '\d+(.\d+)?', + 'DETECTORMINRESOLUTION' => '\d+(.\d+)?', + 'DETECTORROLLMIN' => '-?\d+(.\d+)?', + 'DETECTORROLLMAX' => '-?\d+(.\d+)?', + 'SENSORTHICKNESS' => '\d+', + 'DETECTORSERIALNUMBER' => '[\w\-]+', + 'NUMBEROFPIXELSX' => '\d+', + 'NUMBEROFPIXELSY' => '\d+', + + + // Scan Param Service + 'SCANPARAMETERSSERVICEID' => '\d+', + 'NAME' => '[\w|\s|\-]+', + 'DESCRIPTION' => '.*', + + + // Scan Param Model + 'SCANPARAMETERSMODELID' => '\d+', + 'SEQUENCENUMBER' => '\d+', + 'START' => '\d+(.\d+)?', + 'STOP' => '\d+(.\d+)?', + 'STEP' => '\d+(.\d+)?', + 'ARRAY' => '[\d+(.\d+)?)\s,]+', + + + // DCPlan has Detector + 'DATACOLLECTIONPLANHASDETECTORID' => '\d+', + 'DISTANCE' => '\d+(.\d+)?', + 'ROLL' => '\d+(.\d+)?', + + + // Beamline Setup + 'BEAMLINESETUPID' => '\d+', + 'BEAMLINENAME' => '[\w\-]+', + 'BEAMSIZEXMAX' => '\d+(.\d+)?', + 'BEAMSIZEXMIN' => '\d+(.\d+)?', + 'BEAMSIZEYMAX' => '\d+', + 'BEAMSIZEYMIN' => '\d+', + 'BOXSIZEXMAX' => '\d+(.\d+)?', + 'BOXSIZEXMIN' => '\d+(.\d+)?', + 'BOXSIZEYMAX' => '\d+(.\d+)?', + 'BOXSIZEYMIN' => '\d+(.\d+)?', + 'CS' => '\d+(.\d+)?', + 'ENERGYMAX' => '\d+', + 'ENERGYMIN' => '\d+', + 'GONIOSTATMAXOSCILLATIONWIDTH' => '\d+(.\d+)?', + 'GONIOSTATMINOSCILLATIONWIDTH' => '\d+(.\d+)?', + 'KAPPAMAX' => '-?\d+(.\d+)?', + 'KAPPAMIN' => '-?\d+(.\d+)?', + 'MAXEXPOSURETIMEPERIMAGE' => '\d+(.\d+)?', + 'MINEXPOSURETIMEPERIMAGE' => '\d+(.\d+)?', + 'MAXTRANSMISSION' => '\d+', + 'MINTRANSMISSION' => '\d+', + 'NUMBEROFIMAGESMAX' => '\d+', + 'NUMBEROFIMAGESMIN' => '\d+', + 'OMEGAMAX' => '-?\d+(.\d+)?', + 'OMEGAMIN' => '-?\d+(.\d+)?', + 'PHIMAX' => '-?\d+(.\d+)?', + 'PHIMIN' => '-?\d+(.\d+)?', + 'MONOBANDWIDTHMIN' => '\d+(.\d+)?', + 'MONOBANDWIDTHMAX' => '\d+(.\d+)?', + 'ACTIVE' => '\d', + + 'EXPERIMENTTYPEID' => '\d+', + + ); + + + public static $dispatch = array( array('/plans(/:DIFFRACTIONPLANID)', 'get', '_get_diffraction_plans'), array('/plans', 'post', '_add_diffraction_plan'), array('/plans/:DIFFRACTIONPLANID', 'patch', '_update_diffraction_plan'), @@ -148,161 +146,194 @@ class Exp extends Page array('/setup/:BEAMLINESETUPID', 'patch', '_update_beamline_setup'), array('/experiment/types(/:EXPERIMENTTYPEID)', 'get', '_get_experiment_types') - ); - + ); - function _detectors() { - $where = '1=1'; - $args = array(); - if ($this->has_arg('DETECTORID')) { - $where .= ' AND detectorid=:'.(sizeof($args)+1); - array_push($args, $this->arg('DETECTORID')); - } - - if ($this->has_arg('BEAMLINENAME')) { - $where .= ' AND bls.beamlinename=:'.(sizeof($args)+1); - array_push($args, $this->arg('BEAMLINENAME')); - } + function _detectors() + { + $where = '1=1'; + $args = array(); - $rows = $this->db->pq("SELECT d.detectorid, d.detectortype, d.detectormanufacturer, d.detectorserialnumber, d.sensorthickness, d.detectormodel, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, d.detectordistancemin, d.detectordistancemax, d.density, d.composition, concat(d.detectormanufacturer,' ',d.detectormodel, ' (',d.detectortype,')') as description, d.detectormaxresolution, d.detectorminresolution, count(distinct dc.datacollectionid) as dcs, count(distinct bls.beamlinesetupid) as blsetups, count(distinct dphd.detectorid) as dps,count(distinct dp.detectorid) as dps2, CONCAT(IFNULL(GROUP_CONCAT(distinct ses.beamlinename),''),IFNULL(GROUP_CONCAT(distinct bls.beamlinename),'')) as beamlines, d.numberofpixelsx, d.numberofpixelsy, d.detectorrollmin, d.detectorrollmax - FROM detector d - LEFT OUTER JOIN datacollection dc ON dc.detectorid = d.detectorid - LEFT OUTER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - LEFT OUTER JOIN blsession ses ON ses.sessionid = dcg.sessionid - LEFT OUTER JOIN beamlinesetup bls ON bls.detectorid = d.detectorid - LEFT OUTER JOIN datacollectionplan_has_detector dphd ON dphd.detectorid = d.detectorid - LEFT OUTER JOIN diffractionplan dp ON dp.detectorid = d.detectorid - WHERE $where - GROUP BY d.detectorid", $args); - - if ($this->has_arg('DETECTORID')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such detector'); - - } else $this->_output($rows); + if ($this->has_arg('DETECTORID')) { + $where .= ' AND detectorid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('DETECTORID')); } - - function _add_detector() { - $this->user->can('manage_detector'); - - if (!$this->has_arg('DETECTORTYPE')) $this->_error('No detector type specified'); - if (!$this->has_arg('DETECTORMANUFACTURER')) $this->_error('No detector manufacturer specified'); - if (!$this->has_arg('DETECTORMODEL')) $this->_error('No detector model specified'); - - $args = array($this->arg('DETECTORTYPE'), $this->arg('DETECTORMANUFACTURER'), $this->arg('DETECTORMODEL')); - foreach (array('DETECTORPIXELSIZEHORIZONTAL','DETECTORPIXELSIZEVERTICAL','DETECTORDISTANCEMIN','DETECTORDISTANCEMAX','DENSITY','COMPOSITION','DETECTORMINRESOLUTION','DETECTORMAXRESOLUTION','DETECTORROLLMIN', 'DETECTORROLLMAX', 'SENSORTHICKNESS') as $e) array_push($args, $this->has_arg($e) ? $this->arg($e) : null); - - $this->db->pq("INSERT INTO detector (detectortype,detectormanufacturer,detectormodel,detectorpixelsizehorizontal,detectorpixelsizevertical,detectordistancemin,detectordistancemax,density,composition,detectorminresolution,detectormaxresolution,detectorrollmin,detectorrollmax,sensorthickness) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14)", $args); - - $this->_output(array('DETECTORID' => $this->db->id())); + if ($this->has_arg('BEAMLINENAME')) { + $where .= ' AND bls.beamlinename=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BEAMLINENAME')); } + $tot = $this->db->pq("SELECT count(d.detectorid) as tot + FROM detector d + LEFT OUTER JOIN beamlinesetup bls ON bls.detectorid = d.detectorid + WHERE $where", $args); + $tot = intval($tot[0]['TOT']); - function _update_detector() { - $this->user->can('manage_detector'); - if (!$this->has_arg('DETECTORID')) $this->_error('No detector specified'); + $this->_get_start_end($args); - $chk = $this->db->pq("SELECT detectorid FROM detector WHERE detectorid=:1", array($this->arg('DETECTORID'))); - if (!sizeof($chk)) $this->_error('No such detector'); + $order = $this->_get_order( + array('DETECTORID' => 'd.detectorid'), + 'd.detectorid ASC' + ); - foreach(array('DETECTORTYPE','DETECTORMANUFACTURER','DETECTORMODEL','DETECTORPIXELSIZEHORIZONTAL','DETECTORPIXELSIZEVERTICAL','DETECTORDISTANCEMIN','DETECTORDISTANCEMAX','DENSITY','COMPOSITION','SENSORTHICKNESS','DETECTORSERIALNUMBER','DETECTORMINRESOLUTION','DETECTORMAXRESOLUTION','NUMBEROFPIXELSX','NUMBEROFPIXELSY','DETECTORROLLMIN','DETECTORROLLMAX') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE detector SET '.$f.'=:1 WHERE detectorid=:2', array($this->arg($f), $this->arg('DETECTORID'))); - $this->_output(array($f => $this->arg($f))); - } + $rows = $this->db->paginate("SELECT d.detectorid, d.detectortype, d.detectormanufacturer, d.detectorserialnumber, d.sensorthickness, d.detectormodel, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, d.detectordistancemin, d.detectordistancemax, d.density, d.composition, concat(d.detectormanufacturer,' ',d.detectormodel, ' (',d.detectortype,')') as description, d.detectormaxresolution, d.detectorminresolution, count(distinct dc.datacollectionid) as dcs, count(distinct bls.beamlinesetupid) as blsetups, (SELECT count(distinct dphd.detectorid) FROM DataCollectionPlan_has_Detector dphd WHERE dphd.detectorid = d.detectorid) as dps, GROUP_CONCAT(distinct bls.beamlinename) as beamlines, d.numberofpixelsx, d.numberofpixelsy, d.detectorrollmin, d.detectorrollmax + FROM detector d + LEFT OUTER JOIN datacollection dc ON dc.detectorid = d.detectorid + LEFT OUTER JOIN beamlinesetup bls ON bls.detectorid = d.detectorid + WHERE $where + GROUP BY d.detectorid + ORDER BY $order", $args); + + if ($this->has_arg('DETECTORID')) { + if (sizeof($rows)) $this->_output($rows[0]); + else $this->_error('No such detector'); + } else $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } + + + function _add_detector() + { + $this->haltIfLackingPermission('manage_detector'); + + if (!$this->has_arg('DETECTORTYPE')) + $this->_error('No detector type specified'); + if (!$this->has_arg('DETECTORMANUFACTURER')) + $this->_error('No detector manufacturer specified'); + if (!$this->has_arg('DETECTORMODEL')) + $this->_error('No detector model specified'); + + $args = array($this->arg('DETECTORTYPE'), $this->arg('DETECTORMANUFACTURER'), $this->arg('DETECTORMODEL')); + foreach (array('DETECTORPIXELSIZEHORIZONTAL', 'DETECTORPIXELSIZEVERTICAL', 'DETECTORDISTANCEMIN', 'DETECTORDISTANCEMAX', 'DENSITY', 'COMPOSITION', 'DETECTORMINRESOLUTION', 'DETECTORMAXRESOLUTION', 'DETECTORROLLMIN', 'DETECTORROLLMAX', 'SENSORTHICKNESS') as $e) + array_push($args, $this->has_arg($e) ? $this->arg($e) : null); + + $this->db->pq("INSERT INTO detector (detectortype,detectormanufacturer,detectormodel,detectorpixelsizehorizontal,detectorpixelsizevertical,detectordistancemin,detectordistancemax,density,composition,detectorminresolution,detectormaxresolution,detectorrollmin,detectorrollmax,sensorthickness) VALUES (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14)", $args); + + $this->_output(array('DETECTORID' => $this->db->id())); + } + + + function _update_detector() + { + $this->haltIfLackingPermission('manage_detector'); + if (!$this->has_arg('DETECTORID')) + $this->_error('No detector specified'); + + $chk = $this->db->pq("SELECT detectorid FROM detector WHERE detectorid=:1", array($this->arg('DETECTORID'))); + if (!sizeof($chk)) + $this->_error('No such detector'); + + foreach (array('DETECTORTYPE', 'DETECTORMANUFACTURER', 'DETECTORMODEL', 'DETECTORPIXELSIZEHORIZONTAL', 'DETECTORPIXELSIZEVERTICAL', 'DETECTORDISTANCEMIN', 'DETECTORDISTANCEMAX', 'DENSITY', 'COMPOSITION', 'SENSORTHICKNESS', 'DETECTORSERIALNUMBER', 'DETECTORMINRESOLUTION', 'DETECTORMAXRESOLUTION', 'NUMBEROFPIXELSX', 'NUMBEROFPIXELSY', 'DETECTORROLLMIN', 'DETECTORROLLMAX') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE detector SET ' . $f . '=:1 WHERE detectorid=:2', array($this->arg($f), $this->arg('DETECTORID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _scan_services() { - $where = '1=1'; - $args = array(); + function _scan_services() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('SCANPARAMETERSSERVICEID')) { - $where .= ' AND scanparametersserviceid=:'.(sizeof($args)+1); - array_push($args, $this->arg('SCANPARAMETERSSERVICEID')); - } + if ($this->has_arg('SCANPARAMETERSSERVICEID')) { + $where .= ' AND scanparametersserviceid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SCANPARAMETERSSERVICEID')); + } - $rows = $this->db->pq("SELECT scanparametersserviceid, name, description + $rows = $this->db->pq("SELECT scanparametersserviceid, name, description FROM scanparametersservice WHERE $where", $args); - if ($this->has_arg('SCANPARAMETERSSERVICEID')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such scan service'); - - } else $this->_output($rows); + if ($this->has_arg('SCANPARAMETERSSERVICEID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such scan service'); + } + else + $this->_output($rows); + } + function _add_scan_service() + { + $this->haltIfLackingPermission('manage_service'); - function _add_scan_service() { - $this->user->can('manage_service'); + if (!$this->has_arg('NAME')) + $this->_error('No name specified'); + if (!$this->has_arg('DESCRIPTION')) + $this->_error('No description model specified'); - if (!$this->has_arg('NAME')) $this->_error('No name specified'); - if (!$this->has_arg('DESCRIPTION')) $this->_error('No description model specified'); + $this->db->pq("INSERT INTO scanparametersservice (name,description) VALUES (:1,:2)", + array($this->arg('NAME'), $this->arg('DESCRIPTION'))); - $this->db->pq("INSERT INTO scanparametersservice (name,description) VALUES (:1,:2)", - array($this->arg('NAME'), $this->arg('DESCRIPTION'))); + $this->_output(array('SCANPARAMETERSSERVICEID' => $this->db->id())); + } - $this->_output(array('SCANPARAMETERSSERVICEID' => $this->db->id())); - } + function _update_scan_service() + { + $this->haltIfLackingPermission('manage_service'); + if (!$this->has_arg('SCANPARAMETERSSERVICEID')) + $this->_error('No service specified'); - function _update_scan_service() { - $this->user->can('manage_service'); - if (!$this->has_arg('SCANPARAMETERSSERVICEID')) $this->_error('No service specified'); + $chk = $this->db->pq("SELECT scanparametersserviceid FROM scanparametersservice WHERE scanparametersserviceid=:1", array($this->arg('SCANPARAMETERSSERVICEID'))); + if (!sizeof($chk)) + $this->_error('No such service'); - $chk = $this->db->pq("SELECT scanparametersserviceid FROM scanparametersservice WHERE scanparametersserviceid=:1", array($this->arg('SCANPARAMETERSSERVICEID'))); - if (!sizeof($chk)) $this->_error('No such service'); - - foreach(array('NAME','DESCRIPTION') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE scanparametersservice SET '.$f.'=:1 WHERE scanparametersserviceid=:2', array($this->arg($f), $this->arg('SCANPARAMETERSSERVICEID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('NAME', 'DESCRIPTION') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE scanparametersservice SET ' . $f . '=:1 WHERE scanparametersserviceid=:2', array($this->arg($f), $this->arg('SCANPARAMETERSSERVICEID'))); + $this->_output(array($f => $this->arg($f))); } - } + } - function _scan_models() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $args = array($this->proposalid); - $where = 'WHERE pr.proposalid = :1'; - - if ($this->has_arg('SCANPARAMETERSMODELID')) { - $where .= ' AND m.scanparametersmodelid=:'.(sizeof($args)+1); - array_push($args, $this->arg('SCANPARAMETERSMODELID')); - } + function _scan_models() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - if ($this->has_arg('SCANPARAMETERSSERVICEID')) { - $where .= ' AND m.scanparametersserviceid=:'.(sizeof($args)+1); - array_push($args, $this->arg('SCANPARAMETERSMODELID')); - } + $args = array($this->proposalid); + $where = 'WHERE pr.proposalid = :1'; - if ($this->has_arg('BLSAMPLEID')) { - $where .= ' AND s.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('BLSAMPLEID')); - } - if ($this->has_arg('CONTAINERID')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERID')); - } + if ($this->has_arg('SCANPARAMETERSMODELID')) { + $where .= ' AND m.scanparametersmodelid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SCANPARAMETERSMODELID')); + } + + if ($this->has_arg('SCANPARAMETERSSERVICEID')) { + $where .= ' AND m.scanparametersserviceid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SCANPARAMETERSMODELID')); + } + + if ($this->has_arg('BLSAMPLEID')) { + $where .= ' AND s.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEID')); + } + + if ($this->has_arg('CONTAINERID')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERID')); + } + + if ($this->has_arg('DATACOLLECTIONPLANID')) { + $where .= ' AND (m.datacollectionplanid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('DATACOLLECTIONPLANID')); + } - if ($this->has_arg('DATACOLLECTIONPLANID')) { - $where .= ' AND (m.datacollectionplanid=:'.(sizeof($args)+1); - array_push($args, $this->arg('DATACOLLECTIONPLANID')); - } - - $tot = $this->db->pq("SELECT count(m.scanparametersmodelid) as tot + $tot = $this->db->pq("SELECT count(m.scanparametersmodelid) as tot FROM scanparametersmodel m INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = m.datacollectionplanid @@ -313,16 +344,16 @@ function _scan_models() { INNER JOIN container c ON c.containerid = s.containerid $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('SCANPARAMETERSMODELID' => 'm.scanparametersmodelid'), - 'm.scanparametersmodelid DESC' - ); + $order = $this->_get_order( + array('SCANPARAMETERSMODELID' => 'm.scanparametersmodelid'), + 'm.scanparametersmodelid DESC' + ); - $rows = $this->db->paginate("SELECT m.scanparametersmodelid,sv.name as scanparametersservice, m.scanparametersserviceid, m.datacollectionplanid, s.blsampleid, c.containerid, m.sequencenumber, m.start, m.stop, m.step, m.array + $rows = $this->db->paginate("SELECT m.scanparametersmodelid,sv.name as scanparametersservice, m.scanparametersserviceid, m.datacollectionplanid, s.blsampleid, c.containerid, m.sequencenumber, m.start, m.stop, m.step, m.array FROM scanparametersmodel m INNER JOIN scanparametersservice sv ON sv.scanparametersserviceid = m.scanparametersserviceid INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = m.datacollectionplanid @@ -335,112 +366,132 @@ function _scan_models() { INNER JOIN container c ON c.containerid = s.containerid $where ORDER BY $order", $args); - - if ($this->has_arg('SCANPARAMETERSMODELID')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such scan parameters model'); - - } else $this->_output(array( + + if ($this->has_arg('SCANPARAMETERSMODELID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such scan parameters model'); + + } + else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } + } + + function _add_scan_model() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - function _add_scan_model() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + if (!$this->has_arg('DATACOLLECTIONPLANID')) + $this->_error('No data collection plan specified'); + if (!$this->has_arg('SCANPARAMETERSSERVICEID')) + $this->_error('No parameter model specified'); - if (!$this->has_arg('DATACOLLECTIONPLANID')) $this->_error('No data collection plan specified'); - if (!$this->has_arg('SCANPARAMETERSSERVICEID')) $this->_error('No parameter model specified'); - - $chk = $this->db->pq("SELECT shdp.datacollectionplanid + $chk = $this->db->pq("SELECT shdp.datacollectionplanid FROM blsample_has_datacollectionplan shdp INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE shdp.datacollectionplanid=:1 AND p.proposalid=:2", array($this->arg('DATACOLLECTIONPLANID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan'); + if (!sizeof($chk)) + $this->_error('No such data collection plan'); - $args = array($this->arg('DATACOLLECTIONPLANID'), $this->arg('SCANPARAMETERSSERVICEID')); - foreach(array('SEQUENCENUMBER', 'START', 'STOP', 'STEP', 'ARRAY') as $f) array_push($args, $this->has_arg($f) ? $this->arg($f) : null); + $args = array($this->arg('DATACOLLECTIONPLANID'), $this->arg('SCANPARAMETERSSERVICEID')); + foreach (array('SEQUENCENUMBER', 'START', 'STOP', 'STEP', 'ARRAY') as $f) + array_push($args, $this->has_arg($f) ? $this->arg($f) : null); - $this->db->pq("INSERT INTO scanparametersmodel (datacollectionplanid, scanparametersserviceid, sequencenumber, start, stop, step, array) VALUES (:1,:2,:3,:4,:5,:6,:7)", $args); + $this->db->pq("INSERT INTO scanparametersmodel (datacollectionplanid, scanparametersserviceid, sequencenumber, start, stop, step, array) VALUES (:1,:2,:3,:4,:5,:6,:7)", $args); - $this->_output(array('SCANPARAMETERSMODELID' => $this->db->id())); - } + $this->_output(array('SCANPARAMETERSMODELID' => $this->db->id())); + } - function _update_scan_model() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('SCANPARAMETERSMODELID')) $this->_error('No model specified'); + function _update_scan_model() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('SCANPARAMETERSMODELID')) + $this->_error('No model specified'); - $chk = $this->db->pq("SELECT m.scanparametersmodelid + $chk = $this->db->pq("SELECT m.scanparametersmodelid FROM scanparametersmodel m INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = m.datacollectionplanid INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE m.scanparametersmodelid=:1 AND p.proposalid=:2", array($this->arg('SCANPARAMETERSMODELID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such model'); + if (!sizeof($chk)) + $this->_error('No such model'); - foreach(array('SEQUENCENUMBER','START', 'STOP', 'STEP', 'ARRAY') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE scanparametersmodel SET '.$f.'=:1 WHERE scanparametersmodelid=:2', array($this->arg($f), $this->arg('SCANPARAMETERSMODELID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('SEQUENCENUMBER', 'START', 'STOP', 'STEP', 'ARRAY') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE scanparametersmodel SET ' . $f . '=:1 WHERE scanparametersmodelid=:2', array($this->arg($f), $this->arg('SCANPARAMETERSMODELID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _delete_scan_model() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('SCANPARAMETERSMODELID')) $this->_error('No model specified'); + function _delete_scan_model() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('SCANPARAMETERSMODELID')) + $this->_error('No model specified'); - $chk = $this->db->pq("SELECT m.scanparametersmodelid + $chk = $this->db->pq("SELECT m.scanparametersmodelid FROM scanparametersmodel m INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = m.datacollectionplanid INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE m.scanparametersmodelid=:1 AND p.proposalid=:2", array($this->arg('SCANPARAMETERSMODELID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such model'); + if (!sizeof($chk)) + $this->_error('No such model'); - $this->db->pq("DELETE FROM scanparametersmodel WHERE scanparametersmodelid=:1", array($this->arg('SCANPARAMETERSMODELID'))); + $this->db->pq("DELETE FROM scanparametersmodel WHERE scanparametersmodelid=:1", array($this->arg('SCANPARAMETERSMODELID'))); - $this->_output(new \stdClass); - } + $this->_output(new \stdClass); + } - function _dp_has_detectors() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $args = array($this->proposalid); - $where = 'WHERE pr.proposalid = :1'; - + function _dp_has_detectors() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - if ($this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) { - $where .= ' AND dhd.datacollectionplanhasdetectorid=:'.(sizeof($args)+1); - array_push($args, $this->arg('DATACOLLECTIONPLANHASDETECTORID')); - } + $args = array($this->proposalid); + $where = 'WHERE pr.proposalid = :1'; - if ($this->has_arg('BLSAMPLEID')) { - $where .= ' AND s.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('BLSAMPLEID')); - } - if ($this->has_arg('CONTAINERID')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERID')); - } + if ($this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) { + $where .= ' AND dhd.datacollectionplanhasdetectorid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('DATACOLLECTIONPLANHASDETECTORID')); + } - if ($this->has_arg('DATACOLLECTIONPLANID')) { - $where .= ' AND (d.datacollectionplanid=:'.(sizeof($args)+1); - array_push($args, $this->arg('DATACOLLECTIONPLANID')); - } - + if ($this->has_arg('BLSAMPLEID')) { + $where .= ' AND s.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEID')); + } - $tot = $this->db->pq("SELECT count(dhd.datacollectionplanhasdetectorid) as tot + if ($this->has_arg('CONTAINERID')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERID')); + } + + if ($this->has_arg('DATACOLLECTIONPLANID')) { + $where .= ' AND (d.datacollectionplanid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('DATACOLLECTIONPLANID')); + } + + + $tot = $this->db->pq("SELECT count(dhd.datacollectionplanhasdetectorid) as tot FROM datacollectionplan_has_detector dhd INNER JOIN detector d ON d.detectorid = dhd.detectorid INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = dhd.datacollectionplanid @@ -452,16 +503,16 @@ function _dp_has_detectors() { INNER JOIN container c ON c.containerid = s.containerid $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('DATACOLLECTIONPLANHASDETECTORID' => 'dhd.datacollectionplanhasdetectorid'), - 'dhd.datacollectionplanhasdetectorid DESC' - ); + $order = $this->_get_order( + array('DATACOLLECTIONPLANHASDETECTORID' => 'dhd.datacollectionplanhasdetectorid'), + 'dhd.datacollectionplanhasdetectorid DESC' + ); - $rows = $this->db->paginate("SELECT dhd.datacollectionplanhasdetectorid, d.detectorid, dhd.datacollectionplanid, d.detectormodel, d.detectormanufacturer, d.detectortype, dhd.datacollectionplanid, dhd.exposuretime, dhd.distance, dhd.roll + $rows = $this->db->paginate("SELECT dhd.datacollectionplanhasdetectorid, d.detectorid, dhd.datacollectionplanid, d.detectormodel, d.detectormanufacturer, d.detectortype, dhd.datacollectionplanid, dhd.exposuretime, dhd.distance, dhd.roll FROM datacollectionplan_has_detector dhd INNER JOIN detector d ON d.detectorid = dhd.detectorid INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = dhd.datacollectionplanid @@ -474,106 +525,126 @@ function _dp_has_detectors() { INNER JOIN container c ON c.containerid = s.containerid $where ORDER BY $order", $args); - - if ($this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such data collection plan detector'); - - } else $this->_output(array( + + if ($this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such data collection plan detector'); + + } + else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } + } + + function _dp_add_detector() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - function _dp_add_detector() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + if (!$this->has_arg('DETECTORID')) + $this->_error('No detector specified'); + if (!$this->has_arg('DATACOLLECTIONPLANID')) + $this->_error('No data collection plan specified'); - if (!$this->has_arg('DETECTORID')) $this->_error('No detector specified'); - if (!$this->has_arg('DATACOLLECTIONPLANID')) $this->_error('No data collection plan specified'); - - $chk = $this->db->pq("SELECT shdp.datacollectionplanid + $chk = $this->db->pq("SELECT shdp.datacollectionplanid FROM blsample_has_datacollectionplan shdp INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE shdp.datacollectionplanid=:1 AND p.proposalid=:2", array($this->arg('DATACOLLECTIONPLANID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan'); + if (!sizeof($chk)) + $this->_error('No such data collection plan'); - $args = array($this->arg('DATACOLLECTIONPLANID'), $this->arg('DETECTORID')); - foreach(array('EXPOSURETIME', 'DISTANCE', 'ROLL') as $f) array_push($args, $this->has_arg($f) ? $this->arg($f) : null); + $args = array($this->arg('DATACOLLECTIONPLANID'), $this->arg('DETECTORID')); + foreach (array('EXPOSURETIME', 'DISTANCE', 'ROLL') as $f) + array_push($args, $this->has_arg($f) ? $this->arg($f) : null); - $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposuretime, distance, roll) VALUES (:1,:2,:3,:4,:5)", $args); + $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposuretime, distance, roll) VALUES (:1,:2,:3,:4,:5)", $args); - $this->_output(array('DATACOLLECTIONPLANHASDETECTORID' => $this->db->id())); - } + $this->_output(array('DATACOLLECTIONPLANHASDETECTORID' => $this->db->id())); + } - function _dp_update_detector() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) $this->_error('No data collection plan detector specified'); + function _dp_update_detector() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) + $this->_error('No data collection plan detector specified'); - $chk = $this->db->pq("SELECT dhd.datacollectionplanhasdetectorid + $chk = $this->db->pq("SELECT dhd.datacollectionplanhasdetectorid FROM datacollectionplan_has_detector dhd INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = dhd.datacollectionplanid INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE dhd.datacollectionplanhasdetectorid=:1 AND p.proposalid=:2", array($this->arg('DATACOLLECTIONPLANHASDETECTORID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan detector'); + if (!sizeof($chk)) + $this->_error('No such data collection plan detector'); - foreach(array('EXPOSURETIME', 'DISTANCE', 'ROLL', 'DETECTORID') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE datacollectionplan_has_detector SET '.$f.'=:1 WHERE datacollectionplanhasdetectorid=:2', array($this->arg($f), $this->arg('DATACOLLECTIONPLANHASDETECTORID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('EXPOSURETIME', 'DISTANCE', 'ROLL', 'DETECTORID') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE datacollectionplan_has_detector SET ' . $f . '=:1 WHERE datacollectionplanhasdetectorid=:2', array($this->arg($f), $this->arg('DATACOLLECTIONPLANHASDETECTORID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _dp_remove_detector() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) $this->_error('No data collection plan detector specified'); + function _dp_remove_detector() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('DATACOLLECTIONPLANHASDETECTORID')) + $this->_error('No data collection plan detector specified'); - $chk = $this->db->pq("SELECT dhd.datacollectionplanhasdetectorid + $chk = $this->db->pq("SELECT dhd.datacollectionplanhasdetectorid FROM datacollectionplan_has_detector dhd INNER JOIN blsample_has_datacollectionplan shdp ON shdp.datacollectionplanid = dhd.datacollectionplanid INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE dhd.datacollectionplanhasdetectorid=:1 AND p.proposalid=:2", array($this->arg('DATACOLLECTIONPLANHASDETECTORID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan detector'); + if (!sizeof($chk)) + $this->_error('No such data collection plan detector'); - $this->db->pq("DELETE FROM datacollectionplan_has_detector WHERE datacollectionplanhasdetectorid=:1", array($this->arg('DATACOLLECTIONPLANHASDETECTORID'))); + $this->db->pq("DELETE FROM datacollectionplan_has_detector WHERE datacollectionplanhasdetectorid=:1", array($this->arg('DATACOLLECTIONPLANHASDETECTORID'))); - $this->_output(new \stdClass); - } + $this->_output(new \stdClass); + } - function _get_diffraction_plans() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + function _get_diffraction_plans() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - $where = 'p.proposalid=:1'; - $args = array($this->proposalid); + $where = 'p.proposalid=:1'; + $args = array($this->proposalid); - if ($this->has_arg('DIFFRACTIONPLANID')) { - $where .= ' AND dp.diffractionplanid = :'.(sizeof($args)+1); - array_push($args, $this->arg('did')); - } + if ($this->has_arg('DIFFRACTIONPLANID')) { + $where .= ' AND dp.diffractionplanid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('did')); + } - if ($this->has_arg('BLSAMPLEID')) { - $where .= ' AND s.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('BLSAMPLEID')); - } + if ($this->has_arg('BLSAMPLEID')) { + $where .= ' AND s.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEID')); + } - if ($this->has_arg('CONTAINERID')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERID')); - } + if ($this->has_arg('CONTAINERID')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERID')); + } - $tot = $this->db->pq("SELECT count(dp.diffractionplanid) as tot + $tot = $this->db->pq("SELECT count(dp.diffractionplanid) as tot FROM diffractionplan dp INNER JOIN blsample_has_datacollectionplan hp ON hp.datacollectionplanid = dp.diffractionplanid INNER JOIN blsample s ON s.blsampleid = hp.blsampleid @@ -581,17 +652,17 @@ function _get_diffraction_plans() { INNER JOIN protein p ON p.proteinid = cr.proteinid INNER JOIN container c ON c.containerid = s.containerid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('DIFFRACTIONPLANID' => 'dp.diffractionplanid'), - 'dp.diffractionplanid DESC' - ); + $order = $this->_get_order( + array('DIFFRACTIONPLANID' => 'dp.diffractionplanid'), + 'dp.diffractionplanid DESC' + ); - $rows = $this->db->paginate("SELECT dp.diffractionplanid, dp.comments, dp.experimentkind, dp.preferredbeamsizex, dp.preferredbeamsizey, ROUND(dp.exposuretime, 2) as exposuretime, ROUND(dp.requiredresolution, 2) as requiredresolution, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy as energy, dp.monochromator, dp.monobandwidth, s.blsampleid, s.name as sample, p.proteinid, p.acronym as protein, hp.planorder + $rows = $this->db->paginate("SELECT dp.diffractionplanid, dp.comments, dp.experimentkind, dp.preferredbeamsizex, dp.preferredbeamsizey, ROUND(dp.exposuretime, 2) as exposuretime, ROUND(dp.requiredresolution, 2) as requiredresolution, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy as energy, dp.monochromator, dp.monobandwidth, s.blsampleid, s.name as sample, p.proteinid, p.acronym as protein, hp.planorder FROM diffractionplan dp INNER JOIN blsample_has_datacollectionplan hp ON hp.datacollectionplanid = dp.diffractionplanid INNER JOIN blsample s ON s.blsampleid = hp.blsampleid @@ -601,127 +672,144 @@ function _get_diffraction_plans() { WHERE $where ", $args); - if ($this->has_arg('DIFFRACTIONPLANID')) { - if (sizeof($dps)) $this->_output($dps[0]); - else $this->_error('No such diffraction plan'); + if ($this->has_arg('DIFFRACTIONPLANID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such diffraction plan'); - } else $this->_output(array( + } + else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } + } - function _add_diffraction_plan() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('BLSAMPLEID')) $this->_error('No sample specified'); + function _add_diffraction_plan() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); - $chk = $this->db->pq("SELECT s.blsampleid + $chk = $this->db->pq("SELECT s.blsampleid FROM blsample s INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE s.blsampleid=:1 AND p.proposalid=:2", array($this->arg('BLSAMPLEID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such sample'); + if (!sizeof($chk)) + $this->_error('No such sample'); - $args = array(); - foreach(array('COMMENTS', 'EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'MONOBANDWIDTH') as $f) { - array_push($args, $this->has_arg($f) ? $this->arg($f) : null); - } + $args = array(); + foreach (array('COMMENTS', 'EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'MONOBANDWIDTH') as $f) { + array_push($args, $this->has_arg($f) ? $this->arg($f) : null); + } - $this->db->pq("INSERT INTO diffractionplan (comments, experimentkind, requiredresolution, preferredbeamsizex, preferredbeamsizey, exposuretime, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy, monochromator, monobandwidth) + $this->db->pq("INSERT INTO diffractionplan (comments, experimentkind, requiredresolution, preferredbeamsizex, preferredbeamsizey, exposuretime, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy, monochromator, monobandwidth) VALUES (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15)", $args); - $order = $this->has_arg('PLANORDER') ? $this->arg('PLANORDER') : 1; + $order = $this->has_arg('PLANORDER') ? $this->arg('PLANORDER') : 1; - $dpid = $this->db->id(); - $this->db->pq("INSERT INTO blsample_has_datacollectionplan (blsampleid, datacollectionplanid, planorder) VALUES (:1,:2, :3)", array($this->arg('BLSAMPLEID'), $dpid, $order)); + $dpid = $this->db->id(); + $this->db->pq("INSERT INTO blsample_has_datacollectionplan (blsampleid, datacollectionplanid, planorder) VALUES (:1,:2, :3)", array($this->arg('BLSAMPLEID'), $dpid, $order)); - $this->_output(array('DIFFRACTIONPLANID' => $dpid)); - } + $this->_output(array('DIFFRACTIONPLANID' => $dpid)); + } - function _update_diffraction_plan() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('DIFFRACTIONPLANID')) $this->_error('No data collection plan specified'); + function _update_diffraction_plan() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('DIFFRACTIONPLANID')) + $this->_error('No data collection plan specified'); - $chk = $this->db->pq("SELECT shdp.datacollectionplanid + $chk = $this->db->pq("SELECT shdp.datacollectionplanid FROM blsample_has_datacollectionplan shdp INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE shdp.datacollectionplanid=:1 AND p.proposalid=:2", array($this->arg('DIFFRACTIONPLANID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan'); + if (!sizeof($chk)) + $this->_error('No such data collection plan'); - foreach(array('COMMENTS', 'EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'MONOBANDWIDTH') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE diffractionplan SET '.$f.'=:1 WHERE diffractionplanid=:2', array($this->arg($f), $this->arg('DIFFRACTIONPLANID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('COMMENTS', 'EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'MONOBANDWIDTH') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE diffractionplan SET ' . $f . '=:1 WHERE diffractionplanid=:2', array($this->arg($f), $this->arg('DIFFRACTIONPLANID'))); + $this->_output(array($f => $this->arg($f))); } + } - foreach (array('PLANORDER') as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE blsample_has_datacollectionplan SET $f=:1 WHERE datacollectionplanid=:2", array($this->arg($f), $this->arg('DIFFRACTIONPLANID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('PLANORDER') as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE blsample_has_datacollectionplan SET $f=:1 WHERE datacollectionplanid=:2", array($this->arg($f), $this->arg('DIFFRACTIONPLANID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _delete_diffraction_plan() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('DIFFRACTIONPLANID')) $this->_error('No data collection plan specified'); + function _delete_diffraction_plan() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('DIFFRACTIONPLANID')) + $this->_error('No data collection plan specified'); - $chk = $this->db->pq("SELECT shdp.datacollectionplanid + $chk = $this->db->pq("SELECT shdp.datacollectionplanid FROM blsample_has_datacollectionplan shdp INNER JOIN blsample s ON shdp.blsampleid = s.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein p ON p.proteinid = cr.proteinid WHERE shdp.datacollectionplanid=:1 AND p.proposalid=:2", array($this->arg('DIFFRACTIONPLANID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such data collection plan'); + if (!sizeof($chk)) + $this->_error('No such data collection plan'); - $this->db->pq("DELETE FROM blsample_has_datacollectionplan WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); - $this->db->pq("DELETE FROM datacollectionplan_has_detector WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); - $this->db->pq("DELETE FROM scanparametersmodel WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); - $this->db->pq("DELETE FROM diffractionplan WHERE diffractionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); + $this->db->pq("DELETE FROM blsample_has_datacollectionplan WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); + $this->db->pq("DELETE FROM datacollectionplan_has_detector WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); + $this->db->pq("DELETE FROM scanparametersmodel WHERE datacollectionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); + $this->db->pq("DELETE FROM diffractionplan WHERE diffractionplanid=:1", array($this->arg('DIFFRACTIONPLANID'))); - $this->_output(new \stdClass); - } + $this->_output(new \stdClass); + } - function _get_beamline_setups() { - $where = '1=1'; - $args = array(); + function _get_beamline_setups() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('BEAMLINESETUPID')) { - $where .= ' AND bls.beamlinesetupid = :'.(sizeof($args)+1); - array_push($args, $this->arg('BEAMLINESETUPID')); - } + if ($this->has_arg('BEAMLINESETUPID')) { + $where .= ' AND bls.beamlinesetupid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('BEAMLINESETUPID')); + } - if ($this->has_arg('BEAMLINENAME')) { - $where .= ' AND bls.beamlinename=:'.(sizeof($args)+1); - array_push($args, $this->arg('BEAMLINENAME')); - } + if ($this->has_arg('BEAMLINENAME')) { + $where .= ' AND bls.beamlinename=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BEAMLINENAME')); + } - if ($this->has_arg('ACTIVE')) { - $where .= ' AND bls.active=1'; - } + if ($this->has_arg('ACTIVE')) { + $where .= ' AND bls.active=1'; + } - $tot = $this->db->pq("SELECT count(bls.beamlinesetupid) as tot + $tot = $this->db->pq("SELECT count(bls.beamlinesetupid) as tot FROM beamlinesetup bls WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('BEAMLINESETUPID' => 'bls.beamlinesetupid'), - 'bls.beamlinesetupid DESC' - ); + $order = $this->_get_order( + array('BEAMLINESETUPID' => 'bls.beamlinesetupid'), + 'bls.beamlinesetupid DESC' + ); - $rows = $this->db->paginate("SELECT bls.beamlinesetupid, TO_CHAR(bls.setupdate, 'DD-MM-YYYY') as setupdate, bls.minexposuretimeperimage, bls.goniostatminoscillationwidth, bls.mintransmission, bls.beamlinename, bls.cs, + $rows = $this->db->paginate("SELECT bls.beamlinesetupid, TO_CHAR(bls.setupdate, 'DD-MM-YYYY') as setupdate, bls.minexposuretimeperimage, bls.goniostatminoscillationwidth, bls.mintransmission, bls.beamlinename, bls.cs, bls.active, bls.goniostatmaxoscillationwidth, bls.maxexposuretimeperimage, bls.maxtransmission, bls.beamsizexmin, bls.beamsizexmax, bls.beamsizeymin, bls.beamsizeymax, bls.omegamin, bls.omegamax, bls.kappamin, bls.kappamax, bls.phimin, bls.phimax, bls.energymin, bls.energymax, bls.numberofimagesmin, bls.numberofimagesmax, bls.boxsizexmin, bls.boxsizeymin, bls.boxsizexmax, bls.boxsizeymax, bls.monobandwidthmin, bls.monobandwidthmax, bls.detectorid, d.detectorminresolution, d.detectormaxresolution, d.detectordistancemin, d.detectordistancemax, d.detectorpixelsizehorizontal, d.detectorpixelsizevertical, d.numberofpixelsx, d.numberofpixelsy, d.detectorrollmin, d.detectorrollmax, @@ -733,90 +821,108 @@ function _get_beamline_setups() { GROUP BY bls.beamlinesetupid ", $args); - if ($this->has_arg('BEAMLINESETUPID')) { - if (sizeof($dps)) $this->_output($dps[0]); - else $this->_error('No such beamline setup'); + if ($this->has_arg('BEAMLINESETUPID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such beamline setup'); - } else $this->_output(array( + } + else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } + } - function _add_beamline_setup() { - if (!$this->user->can('manage_params')) $this->_error('You do not have permission to add beamline parameters'); - if (!$this->has_arg('BEAMLINENAME')) $this->_error('No beamline specified'); - if (!$this->has_arg('DETECTORID')) $this->_error('No detector specified'); + function _add_beamline_setup() + { + $this->haltIfLackingPermission('manage_params'); + if (!$this->has_arg('BEAMLINENAME')) + $this->_error('No beamline specified'); + if (!$this->has_arg('DETECTORID')) + $this->_error('No detector specified'); - $args = array($this->arg('BEAMLINENAME'), $this->arg('DETECTORID')); - foreach (array('BEAMSIZEXMAX', 'BEAMSIZEXMIN', 'BEAMSIZEYMAX', 'BEAMSIZEYMIN', 'BOXSIZEXMAX', 'BOXSIZEXMIN', 'BOXSIZEYMAX', 'BOXSIZEYMIN', 'CS', 'ENERGYMAX', 'ENERGYMIN', 'GONIOSTATMAXOSCILLATIONWIDTH', 'GONIOSTATMINOSCILLATIONWIDTH', 'KAPPAMAX', 'KAPPAMIN', 'MAXEXPOSURETIMEPERIMAGE', 'MINEXPOSURETIMEPERIMAGE', 'MAXTRANSMISSION', 'MINTRANSMISSION', 'NUMBEROFIMAGESMAX', 'NUMBEROFIMAGESMIN', 'OMEGAMAX', 'OMEGAMIN', 'PHIMAX', 'PHIMIN', 'MONOBANDWIDTHMIN', 'MONOBANDWIDTHMAX') as $e) array_push($args, $this->has_arg($e) ? $this->arg($e) : null); + $args = array($this->arg('BEAMLINENAME'), $this->arg('DETECTORID')); + foreach (array('BEAMSIZEXMAX', 'BEAMSIZEXMIN', 'BEAMSIZEYMAX', 'BEAMSIZEYMIN', 'BOXSIZEXMAX', 'BOXSIZEXMIN', 'BOXSIZEYMAX', 'BOXSIZEYMIN', 'CS', 'ENERGYMAX', 'ENERGYMIN', 'GONIOSTATMAXOSCILLATIONWIDTH', 'GONIOSTATMINOSCILLATIONWIDTH', 'KAPPAMAX', 'KAPPAMIN', 'MAXEXPOSURETIMEPERIMAGE', 'MINEXPOSURETIMEPERIMAGE', 'MAXTRANSMISSION', 'MINTRANSMISSION', 'NUMBEROFIMAGESMAX', 'NUMBEROFIMAGESMIN', 'OMEGAMAX', 'OMEGAMIN', 'PHIMAX', 'PHIMIN', 'MONOBANDWIDTHMIN', 'MONOBANDWIDTHMAX') as $e) + array_push($args, $this->has_arg($e) ? $this->arg($e) : null); - $this->db->pq("INSERT INTO beamlinesetup (setupdate,beamlinename,detectorid, + $this->db->pq("INSERT INTO beamlinesetup (setupdate,beamlinename,detectorid, beamsizexmin,beamsizexmax,beamsizeymin,beamsizeymax,boxsizexmax,boxsizexmin,boxsizeymax,boxsizeymin,cs,energymax,energymin,goniostatmaxoscillationwidth,goniostatminoscillationwidth,kappamax,kappamin,maxexposuretimeperimage,minexposuretimeperimage,maxtransmission,mintransmission,numberofimagesmax,numberofimagesmin,omegamax,omegamin,phimax,phimin,active,monobandwidthmin,monobandwidthmax) VALUES (CURRENT_TIMESTAMP,:1,:2 ,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16,:17,:18,:19,:20,:21,:22,:23,:24,:25,:26,:27,0,:28,:29)", $args); - $this->_output(array('BEAMLINESETUPID' => $this->db->id(), 'SETUPDATE' => date('d-m-Y'))); - } + $this->_output(array('BEAMLINESETUPID' => $this->db->id(), 'SETUPDATE' => date('d-m-Y'))); + } - function _update_beamline_setup() { - if (!$this->user->can('manage_params')) $this->_error('You do not have permission to update beamline parameters'); - if (!$this->has_arg('BEAMLINESETUPID')) $this->_error('No beamline setup specified'); + function _update_beamline_setup() + { + $this->haltIfLackingPermission('manage_params'); + if (!$this->has_arg('BEAMLINESETUPID')) + $this->_error('No beamline setup specified'); - $chk = $this->db->pq("SELECT beamlinesetupid, detectorid, beamlinename + $chk = $this->db->pq("SELECT beamlinesetupid, detectorid, beamlinename FROM beamlinesetup WHERE beamlinesetupid=:1", array($this->arg('BEAMLINESETUPID'))); - if (!sizeof($chk)) $this->_error('No such beamline setup'); + if (!sizeof($chk)) + $this->_error('No such beamline setup'); - foreach(array('DETECTORID', 'BEAMLINENAME', 'BEAMSIZEXMAX', 'BEAMSIZEXMIN', 'BEAMSIZEYMAX', 'BEAMSIZEYMIN', 'BOXSIZEXMAX', 'BOXSIZEXMIN', 'BOXSIZEYMAX', 'BOXSIZEYMIN', 'CS', 'ENERGYMAX', 'ENERGYMIN', 'GONIOSTATMAXOSCILLATIONWIDTH', 'GONIOSTATMINOSCILLATIONWIDTH', 'KAPPAMAX', 'KAPPAMIN', 'MAXEXPOSURETIMEPERIMAGE', 'MINEXPOSURETIMEPERIMAGE', 'MAXTRANSMISSION', 'MINTRANSMISSION', 'NUMBEROFIMAGESMAX', 'NUMBEROFIMAGESMIN', 'OMEGAMAX', 'OMEGAMIN', 'PHIMAX', 'PHIMIN', 'ACTIVE', 'MONOBANDWIDTHMIN', 'MONOBANDWIDTHMAX') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE beamlinesetup SET '.$f.'=:1 WHERE beamlinesetupid=:2', array($this->arg($f), $this->arg('BEAMLINESETUPID'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('DETECTORID', 'BEAMLINENAME', 'BEAMSIZEXMAX', 'BEAMSIZEXMIN', 'BEAMSIZEYMAX', 'BEAMSIZEYMIN', 'BOXSIZEXMAX', 'BOXSIZEXMIN', 'BOXSIZEYMAX', 'BOXSIZEYMIN', 'CS', 'ENERGYMAX', 'ENERGYMIN', 'GONIOSTATMAXOSCILLATIONWIDTH', 'GONIOSTATMINOSCILLATIONWIDTH', 'KAPPAMAX', 'KAPPAMIN', 'MAXEXPOSURETIMEPERIMAGE', 'MINEXPOSURETIMEPERIMAGE', 'MAXTRANSMISSION', 'MINTRANSMISSION', 'NUMBEROFIMAGESMAX', 'NUMBEROFIMAGESMIN', 'OMEGAMAX', 'OMEGAMIN', 'PHIMAX', 'PHIMIN', 'ACTIVE', 'MONOBANDWIDTHMIN', 'MONOBANDWIDTHMAX') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE beamlinesetup SET ' . $f . '=:1 WHERE beamlinesetupid=:2', array($this->arg($f), $this->arg('BEAMLINESETUPID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _get_start_end(&$args, $default=15) { - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : $default; - $start = 0; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - array_push($args, $start); - array_push($args, $end); - } + function _get_start_end(&$args, $default = 15) + { + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : $default; + $start = 0; + $end = $pp; - function _get_order($cols, $default) { - if ($this->has_arg('sort_by')) { - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) return $cols[$this->arg('sort_by')].' '.$dir; - } else return $default; + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; } - // Get active experiment types from database - function _get_experiment_types() { - $where = 'active = 1'; - $args = array(); + array_push($args, $start); + array_push($args, $end); + } - if ($this->has_arg('EXPERIMENTTYPEID')) { - $where .= ' AND experimenttypeid = :'.(sizeof($args)+1); - array_push($args, $this->arg('EXPERIMENTTYPEID')); - } + function _get_order($cols, $default) + { + if ($this->has_arg('sort_by')) { + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + return $cols[$this->arg('sort_by')] . ' ' . $dir; + } + else + return $default; + } + + // Get active experiment types from database + function _get_experiment_types() + { + $where = 'active = 1'; + $args = array(); + + if ($this->has_arg('EXPERIMENTTYPEID')) { + $where .= ' AND experimenttypeid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('EXPERIMENTTYPEID')); + } - $rows = $this->db->pq("SELECT experimenttypeid, name, proposaltype + $rows = $this->db->pq("SELECT experimenttypeid, name, proposaltype FROM ExperimentType WHERE $where", $args); - if (!sizeof($rows)) $this->_error('No experiment types found'); + if (!sizeof($rows)) + $this->_error('No experiment types found'); - $this->_output(array('total' => count($rows), 'data' => $rows)); - } -} + $this->_output(array('total' => count($rows), 'data' => $rows)); + } +} \ No newline at end of file diff --git a/api/src/Page/Fault.php b/api/src/Page/Fault.php index 201507b5f..1c7f58004 100644 --- a/api/src/Page/Fault.php +++ b/api/src/Page/Fault.php @@ -6,161 +6,183 @@ class Fault extends Page { - - public static $arg_list = array('time' => '\d+', - 'bl' => '[\w-]+', - 'sid' => '\d+', - 'cid' => '\d+', - 'scid' => '\d+', - 'fid' => '\d+', - 'runid' => '\d+', - - 'NAME' => '[A-Za-z0-9_\- ]+', - 'BLS' => '[\w-]+', - 'SYSTEMID' => '\d+', - 'COMPONENTID' => '\d+', - - 'term' => '\w+', - 'visit' => '\w+\d+-\d+', - - 'BEAMLINE' => '[\w-]+', - 'STARTTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'ENDTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'BEAMTIMELOST_STARTTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'BEAMTIMELOST_ENDTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'SUBCOMPONENTID' => '\d+', - 'BEAMTIMELOST' => '\d', - 'RESOLVED' => '\d', - 'SESSIONID' => '\d+', - - 'TITLE' => '.*', - 'DESCRIPTION' => '.*', - 'RESOLUTION' => '.*', - - 'ASSIGNEEID' => '\d+', - ); - - - public static $dispatch = array(array('(/:fid)', 'get', '_get_faults'), - array('', 'post', '_add_fault'), - array('/:fid', 'patch', '_update_fault'), - - array('/bls', 'get', '_get_beamlines'), - - array('/sys', 'get', '_get_systems'), - array('/sys', 'post', '_add_system'), - array('/sys/:sid', 'put', '_update_system'), - - array('/com', 'get', '_get_components'), - array('/com', 'post', '_add_component'), - array('/com/:cid', 'put', '_update_component'), - - array('/scom', 'get', '_get_subcomponents'), - array('/scom', 'post', '_add_subcomponent'), - array('/scom/:scid', 'put', '_update_subcomponent'), - - // array('/migrate', 'get', '_migrate'), - ); - - function _migrate() { - $faults = $this->db->pq("SELECT faultid, owner, assignee FROM bf_fault WHERE personid IS NULL"); - foreach ($faults as $f) { - $owner = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($f['OWNER'])); - if (sizeof($owner)) { - $this->db->pq("UPDATE bf_fault SET personid=:1 WHERE faultid=:2", array($owner[0]['PERSONID'], $f['FAULTID'])); - } + public static $arg_list = array('time' => '\d+', + 'bl' => '[\w\-]+', + 'sid' => '\d+', + 'cid' => '\d+', + 'scid' => '\d+', + 'fid' => '\d+', + 'runid' => '\d+', + + 'NAME' => '[A-Za-z0-9_\- ]+', + 'BLS' => '[\w\-]+', + 'SYSTEMID' => '\d+', + 'COMPONENTID' => '\d+', + + 'term' => '\w+', + 'visit' => '\w+\d+-\d+', + + 'BEAMLINE' => '[\w\-]+', + 'STARTTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'ENDTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'BEAMTIMELOST_STARTTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'BEAMTIMELOST_ENDTIME' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'SUBCOMPONENTID' => '\d+', + 'BEAMTIMELOST' => '\d', + 'RESOLVED' => '\d', + 'SESSIONID' => '\d+', + + 'TITLE' => '.*', + 'DESCRIPTION' => '.*', + 'RESOLUTION' => '.*', + + 'ASSIGNEEID' => '\d+', + ); + + + public static $dispatch = array(array('(/:fid)', 'get', '_get_faults'), + array('', 'post', '_add_fault'), + array('/:fid', 'patch', '_update_fault'), + + array('/bls', 'get', '_get_beamlines'), + + array('/sys', 'get', '_get_systems'), + array('/sys', 'post', '_add_system'), + array('/sys/:sid', 'put', '_update_system'), + + array('/com', 'get', '_get_components'), + array('/com', 'post', '_add_component'), + array('/com/:cid', 'put', '_update_component'), + + array('/scom', 'get', '_get_subcomponents'), + array('/scom', 'post', '_add_subcomponent'), + array('/scom/:scid', 'put', '_update_subcomponent'), + + // array('/migrate', 'get', '_migrate'), + ); + + + function _migrate() + { + $faults = $this->db->pq("SELECT faultid, owner, assignee FROM bf_fault WHERE personid IS NULL"); + foreach ($faults as $f) + { + $owner = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($f['OWNER'])); + if (sizeof($owner)) + { + $this->db->pq("UPDATE bf_fault SET personid=:1 WHERE faultid=:2", array($owner[0]['PERSONID'], $f['FAULTID'])); + } - if ($f['ASSIGNEE']) { - $assignee = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($f['ASSIGNEE'])); - if (sizeof($assignee)) { - $this->db->pq("UPDATE bf_fault SET assigneeid=:1 WHERE faultid=:2", array($assignee[0]['PERSONID'], $f['FAULTID'])); - } + if ($f['ASSIGNEE']) + { + $assignee = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($f['ASSIGNEE'])); + if (sizeof($assignee)) + { + $this->db->pq("UPDATE bf_fault SET assigneeid=:1 WHERE faultid=:2", array($assignee[0]['PERSONID'], $f['FAULTID'])); } } } + } + + + # ------------------------------------------------------------------------ + # Return faults based on search terms / filters + function _get_faults() + { + $args = array(); + $where = array(); + $join = ''; + + if (!$this->user->hasPermission('fault_view')) + { + array_push($where, 'shp.personid=:' . (sizeof($args) + 1)); + array_push($args, $this->user->personId); + $join = 'INNER JOIN session_has_person shp ON shp.sessionid = bl.sessionid'; + } - - # ------------------------------------------------------------------------ - # Return faults based on search terms / filters - function _get_faults() { - $args = array(); - $where = array(); - $join = ''; - - if (!$this->user->has('fault_view')) { - array_push($where, 'shp.personid=:'.(sizeof($args)+1)); - array_push($args, $this->user->personid); - $join = 'INNER JOIN session_has_person shp ON shp.sessionid = bl.sessionid'; - } + if ($this->has_arg('s')) + { + $st = sizeof($args) + 1; + array_push($where, "(lower(f.title) LIKE lower(CONCAT(CONCAT('%',:" . $st . "),'%')) OR lower(f.description) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "),'%')) OR lower(s.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "),'%')) OR lower(c.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 3) . "),'%')) OR lower(sc.name) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 4) . "),'%')))"); + for ($i = 0; $i < 5; $i++) + array_push($args, $this->arg('s')); + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - array_push($where, "(lower(f.title) LIKE lower(CONCAT(CONCAT('%',:".$st."),'%')) OR lower(f.description) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."),'%')) OR lower(s.name) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."),'%')) OR lower(c.name) LIKE lower(CONCAT(CONCAT('%',:".($st+3)."),'%')) OR lower(sc.name) LIKE lower(CONCAT(CONCAT('%',:".($st+4)."),'%')))"); - for ($i = 0; $i < 5; $i++) array_push($args, $this->arg('s')); - } - - $ext_columns = ''; - if ($this->has_arg('fid')) { - array_push($where, 'f.faultid=:'.(sizeof($args) +1)); - array_push($args, $this->arg('fid')); - $ext_columns = 'f.description, f.resolution,'; - } + $ext_columns = ''; + if ($this->has_arg('fid')) + { + array_push($where, 'f.faultid=:' . (sizeof($args) + 1)); + array_push($args, $this->arg('fid')); + $ext_columns = 'f.description, f.resolution,'; + } - if ($this->has_arg('visit')) { - array_push($where, "CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber),'-'),bl.visit_number) LIKE :".(sizeof($args)+1)); - array_push($args, $this->arg('visit')); - } - - - if ($this->has_arg('sid')) { - array_push($where, 's.systemid=:'.(sizeof($args) + 1)); - array_push($args, $this->arg('sid')); - } + if ($this->has_arg('visit')) + { + array_push($where, "CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) LIKE :" . (sizeof($args) + 1)); + array_push($args, $this->arg('visit')); + } - if ($this->has_arg('cid')) { - array_push($where, 'c.componentid=:'.(sizeof($args) + 1)); - array_push($args, $this->arg('cid')); - } - - if ($this->has_arg('scid')) { - array_push($where, 'sc.subcomponentid=:'.(sizeof($args) + 1)); - array_push($args, $this->arg('scid')); - } - if ($this->has_arg('bl')) { - if ($this->arg('bl') == 'P01') { - $bls = array(); - foreach (array('i02', 'i03', 'i04') as $b) { - array_push($bls, 'bl.beamlinename LIKE :'.(sizeof($args) + 1)); - array_push($args, $b); - } - array_push($where, '('.implode($bls, ' OR ').')'); - } else { - array_push($where, 'bl.beamlinename LIKE :'.(sizeof($args) + 1)); - array_push($args, $this->arg('bl')); - } - } + if ($this->has_arg('sid')) + { + array_push($where, 's.systemid=:' . (sizeof($args) + 1)); + array_push($args, $this->arg('sid')); + } - if ($this->has_arg('runid')) { - array_push($where, 'vr.runid = :' . (sizeof($args)+1)); - array_push($args, $this->arg('runid')); + if ($this->has_arg('cid')) + { + array_push($where, 'c.componentid=:' . (sizeof($args) + 1)); + array_push($args, $this->arg('cid')); + } + + if ($this->has_arg('scid')) + { + array_push($where, 'sc.subcomponentid=:' . (sizeof($args) + 1)); + array_push($args, $this->arg('scid')); + } + + if ($this->has_arg('bl')) + { + if ($this->arg('bl') == 'P01') + { + $bls = array(); + foreach (array('i02', 'i03', 'i04') as $b) + { + array_push($bls, 'bl.beamlinename LIKE :' . (sizeof($args) + 1)); + array_push($args, $b); + } + array_push($where, '(' . implode($bls, ' OR ') . ')'); } - - $where = implode($where, ' AND '); - if ($where) $where = ' WHERE ' . $where; - - $start = 0; - $pp = $this->has_arg('pp') ? $this->arg('pp') : 20; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; + else + { + array_push($where, 'bl.beamlinename LIKE :' . (sizeof($args) + 1)); + array_push($args, $this->arg('bl')); } - - $tot = $this->db->pq("SELECT count(faultid) as tot + } + + if ($this->has_arg('runid')) + { + array_push($where, 'vr.runid = :' . (sizeof($args) + 1)); + array_push($args, $this->arg('runid')); + } + + $where = implode($where, ' AND '); + if ($where) + $where = ' WHERE ' . $where; + + $start = 0; + $pp = $this->has_arg('pp') ? $this->arg('pp') : 20; + $end = $pp; + + if ($this->has_arg('page')) + { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $tot = $this->db->pq("SELECT count(faultid) as tot FROM bf_fault f INNER JOIN bf_subcomponent sc ON f.subcomponentid = sc.subcomponentid INNER JOIN bf_component c ON sc.componentid = c.componentid @@ -170,16 +192,17 @@ function _get_faults() { INNER JOIN proposal p on p.proposalid = bl.proposalid $join $where", $args); - $tot = $tot[0]['TOT']; - - $pgs = intval($tot/$pp); - if ($tot % $pp != 0) $pgs++; - - $st = sizeof($args) + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT $ext_columns f.personid, f.assigneeid, CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as name, CONCAT(CONCAT(asi.givenname, ' '), asi.familyname) as assignee, f.faultid, f.sessionid, f.elogid, f.attachment, CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber),'-'),bl.visit_number) as visit, bl.beamlinename as beamline, s.systemid, s.name as system, c.componentid, c.name as component, f.subcomponentid, sc.name as subcomponent, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI') as starttime, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI') as endtime, f.beamtimelost, round(TIMESTAMPDIFF('MINUTE',f.beamtimelost_starttime, f.beamtimelost_endtime)/60,2) as lost, f.title, f.resolved, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI') as beamtimelost_endtime, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI') as beamtimelost_starttime + $tot = $tot[0]['TOT']; + + $pgs = intval($tot / $pp); + if ($tot % $pp != 0) + $pgs++; + + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT $ext_columns f.personid, f.assigneeid, CONCAT(pe.givenname, ' ', pe.familyname) as name, CONCAT(asi.givenname, ' ', asi.familyname) as assignee, f.faultid, f.sessionid, f.elogid, f.attachment, CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) as visit, bl.beamlinename as beamline, s.systemid, s.name as system, c.componentid, c.name as component, f.subcomponentid, sc.name as subcomponent, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI') as starttime, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI') as endtime, f.beamtimelost, round(TIMESTAMPDIFF('MINUTE',f.beamtimelost_starttime, f.beamtimelost_endtime)/60,2) as lost, f.title, f.resolved, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI') as beamtimelost_endtime, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI') as beamtimelost_starttime FROM bf_fault f INNER JOIN person pe ON pe.personid = f.personid LEFT OUTER JOIN person asi ON asi.personid = f.assigneeid @@ -193,333 +216,419 @@ function _get_faults() { $where GROUP BY f.faultid ORDER BY f.starttime DESC", $args); - - foreach ($rows as &$r) { - foreach (array('DESCRIPTION', 'RESOLUTION') as $k) { - if (array_key_exists($k, $r)) { - $r[$k] = $this->db->read($r[$k]); - } - } - $r['ATTACH_IMAGE'] = false; - if ($r['ATTACHMENT']) { - $r['ATTACHMENT'] = basename($r['ATTACHMENT']); - $ext = pathinfo($r['ATTACHMENT'], PATHINFO_EXTENSION); - - if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) $r['ATTACH_IMAGE'] = true; + foreach ($rows as &$r) + { + foreach (array('DESCRIPTION', 'RESOLUTION') as $k) + { + if (array_key_exists($k, $r)) + { + $r[$k] = $this->db->read($r[$k]); } } - - if ($this->has_arg('fid')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such fault'); - - } else $this->_output(array($pgs, $rows)); + + $r['ATTACH_IMAGE'] = false; + if ($r['ATTACHMENT']) + { + $r['ATTACHMENT'] = basename($r['ATTACHMENT']); + $ext = pathinfo($r['ATTACHMENT'], PATHINFO_EXTENSION); + + if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) + $r['ATTACH_IMAGE'] = true; + } + } + + if ($this->has_arg('fid')) + { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such fault'); + + } + else + $this->_output(array($pgs, $rows)); + } + + + # ------------------------------------------------------------------------ + # Update fields for a fault + function _update_fault() + { + $check = $this->db->pq('SELECT personid,assigneeid FROM bf_fault WHERE faultid=:1', array($this->arg('fid'))); + if (!sizeof($check)) + $this->_error('A fault with that id doesnt exists'); + $check = $check[0]; + + if ($this->user->personId != $check['PERSONID'] && $this->user->personId != $check['ASSIGNEEID']) + { + $this->haltIfLackingPermission('fault_global'); } - - - # ------------------------------------------------------------------------ - # Update fields for a fault - function _update_fault() { - $check = $this->db->pq('SELECT personid,assigneeid FROM bf_fault WHERE faultid=:1', array($this->arg('fid'))); - if (!sizeof($check)) $this->_error('A fault with that id doesnt exists'); - $check = $check[0]; - - if ($this->user->personid != $check['PERSONID'] && $this->user->personid != $check['ASSIGNEEID'] && !$this->user->can('fault_global')) $this->_error('You cant edit that fault report'); - - - $fields = array('TITLE', 'STARTTIME', 'ENDTIME', 'BEAMTIMELOST_STARTTIME', 'BEAMTIMELOST_ENDTIME', 'SESSIONID', 'SUBCOMPONENTID', 'BEAMTIMELOST', 'RESOLVED', 'RESOLUTION', 'DESCRIPTION'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $fl = ':1'; - if (in_array($f, array('STARTTIME', 'ENDTIME', 'BEAMTIMELOST_STARTTIME', 'BEAMTIMELOST_ENDTIME'))) { - $fl = "TO_DATE(:1, 'DD-MM-YYYY HH24:MI')"; - } - $this->db->pq("UPDATE bf_fault SET $f=$fl WHERE faultid=:2", array($this->arg($f), $this->arg('fid'))); + $fields = array('TITLE', 'STARTTIME', 'ENDTIME', 'BEAMTIMELOST_STARTTIME', 'BEAMTIMELOST_ENDTIME', 'SESSIONID', 'SUBCOMPONENTID', 'BEAMTIMELOST', 'RESOLVED', 'RESOLUTION', 'DESCRIPTION'); + foreach ($fields as $f) + { + if ($this->has_arg($f)) + { + $fl = ':1'; + if (in_array($f, array('STARTTIME', 'ENDTIME', 'BEAMTIMELOST_STARTTIME', 'BEAMTIMELOST_ENDTIME'))) + { + $fl = "TO_DATE(:1, 'DD-MM-YYYY HH24:MI')"; + } - if ($f == 'SESSIONID') { - $v = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber),'-'),bl.visit_number) as visit FROM blsession bl INNER JOIN proposal p ON bl.proposalid = p.proposalid WHERE bl.sessionid=:1", array($this->arg($f))); - if (sizeof($v)) { - $this->_output(array('VISIT' => $v[0]['VISIT'])); - } + $this->db->pq("UPDATE bf_fault SET $f=$fl WHERE faultid=:2", array($this->arg($f), $this->arg('fid'))); - } else if ($f == 'SUBCOMPONENTID') { - $sc = $this->db->pq('SELECT name FROM bf_subcomponent WHERE subcomponentid=:1', array($this->arg($f))); - if (sizeof($sc)) { - $this->_output(array('SUBCOMPONENT' => $sc[0]['NAME'])); - } + if ($f == 'SESSIONID') + { + $v = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) as visit FROM blsession bl INNER JOIN proposal p ON bl.proposalid = p.proposalid WHERE bl.sessionid=:1", array($this->arg($f))); + if (sizeof($v)) + { + $this->_output(array('VISIT' => $v[0]['VISIT'])); + } - } else $this->_output(array($f => $this->arg($f))); + } + else if ($f == 'SUBCOMPONENTID') + { + $sc = $this->db->pq('SELECT name FROM bf_subcomponent WHERE subcomponentid=:1', array($this->arg($f))); + if (sizeof($sc)) + { + $this->_output(array('SUBCOMPONENT' => $sc[0]['NAME'])); + } } + else + $this->_output(array($f => $this->arg($f))); + } } + } + + + # ------------------------------------------------------------------------ + # Return a list of beamlines with ids + function _get_beamlines() + { + $row_tmp = $this->db->pq("SELECT distinct beamlinename as name FROM blsession WHERE beamlinename NOT LIKE 'i04 1' ORDER BY beamlinename"); + $rows = array(); + foreach ($row_tmp as $r) + { + array_push($rows, array('NAME' => $r['NAME'])); + } - - # ------------------------------------------------------------------------ - # Return a list of beamlines with ids - function _get_beamlines() { - $row_tmp = $this->db->pq("SELECT distinct beamlinename as name FROM blsession WHERE beamlinename NOT LIKE 'i04 1' ORDER BY beamlinename"); - $rows = array(); - foreach ($row_tmp as $r) { - array_push($rows, array('NAME' => $r['NAME'])); + // $rows = array(array('NAME' => 'i02'), array('NAME' => 'i03'), array('NAME' => 'i04'), array('NAME' => 'i04-1'), array('NAME' => 'i24')); + + $bls = array(); + foreach ($rows as $r) + $bls[$r['NAME']] = $r['NAME']; + $this->_output($this->has_arg('array') ? $bls : $rows); + } + + # ------------------------------------------------------------------------ + # Return a list of sytems for a beamline + function _get_systems() + { + $args = array(); + + if ($this->has_arg('bl')) + { + $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); + + $blw = array(); + foreach ($bls as $b) + { + array_push($blw, 'hs.beamlinename LIKE :' . (sizeof($args) + 1)); + array_push($args, $b); } - // $rows = array(array('NAME' => 'i02'), array('NAME' => 'i03'), array('NAME' => 'i04'), array('NAME' => 'i04-1'), array('NAME' => 'i24')); - - $bls = array(); - foreach ($rows as $r) $bls[$r['NAME']] = $r['NAME']; - $this->_output($this->has_arg('array') ? $bls : $rows); - } - - # ------------------------------------------------------------------------ - # Return a list of sytems for a beamline - function _get_systems() { - $args = array(); - - if ($this->has_arg('bl')) { - $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); - - $blw = array(); - foreach ($bls as $b) { - array_push($blw, 'hs.beamlinename LIKE :'.(sizeof($args) + 1)); - array_push($args, $b); - } - - $where = ' WHERE ('.implode($blw, ' OR ').')'; - - } else $where = ''; - - $rows = $this->db->pq("SELECT s.systemid, s.name, s.description, string_agg(hs.beamlinename) as beamlines FROM bf_system s INNER JOIN bf_system_beamline hs ON s.systemid = hs.systemid ".$where." GROUP BY s.systemid, s.name, s.description ORDER BY s.name", $args); - - $sys = array(); - foreach ($rows as $s) $sys[$s['SYSTEMID']] = $s['NAME']; - - $this->_output($this->has_arg('array') ? $sys : $rows); + $where = ' WHERE (' . implode($blw, ' OR ') . ')'; + } - - # ------------------------------------------------------------------------ - # Return a list of components for a system on a beamline - function _get_components() { - if (!$this->has_arg('sid')) $this->_error('No systemid specified'); - $args = array($this->arg('sid')); - - if ($this->has_arg('bl')) { - $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); - - $blw = array(); - foreach ($bls as $b) { - array_push($blw, 'hc.beamlinename LIKE :'.(sizeof($args) + 1)); - array_push($args, $b); - } - - $where = ' AND ('.implode($blw, ' OR ').')'; - } else $where = ''; - - $rows = $this->db->pq('SELECT c.componentid, c.name, c.description, string_agg(hc.beamlinename) as beamlines FROM bf_component c INNER JOIN bf_component_beamline hc ON c.componentid = hc.componentid WHERE c.systemid=:1'.$where.' GROUP BY c.componentid, c.name, c.description ORDER BY beamlines,c.name', $args); - - $com = array(); - foreach ($rows as $c) $com[$c['COMPONENTID']] = $c['NAME']; - - $this->_output($this->has_arg('array') ? $com : $rows); + else + $where = ''; + + $rows = $this->db->pq("SELECT s.systemid, s.name, s.description, string_agg(hs.beamlinename) as beamlines FROM bf_system s INNER JOIN bf_system_beamline hs ON s.systemid = hs.systemid " . $where . " GROUP BY s.systemid, s.name, s.description ORDER BY s.name", $args); + + $sys = array(); + foreach ($rows as $s) + $sys[$s['SYSTEMID']] = $s['NAME']; + + $this->_output($this->has_arg('array') ? $sys : $rows); + } + + # ------------------------------------------------------------------------ + # Return a list of components for a system on a beamline + function _get_components() + { + if (!$this->has_arg('sid')) + $this->_error('No systemid specified'); + $args = array($this->arg('sid')); + + if ($this->has_arg('bl')) + { + $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); + + $blw = array(); + foreach ($bls as $b) + { + array_push($blw, 'hc.beamlinename LIKE :' . (sizeof($args) + 1)); + array_push($args, $b); + } + + $where = ' AND (' . implode($blw, ' OR ') . ')'; } - - # ------------------------------------------------------------------------ - # Return a list of subcomponents for a component on a beamline - function _get_subcomponents() { - if (!$this->has_arg('cid')) $this->_error('No componentid specified'); - $args = array($this->arg('cid')); - - if ($this->has_arg('bl')) { - $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); - - $blw = array(); - foreach ($bls as $b) { - array_push($blw, 'hs.beamlinename LIKE :'.(sizeof($args) + 1)); - array_push($args, $b); - } - - $where = ' AND ('.implode($blw, ' OR ').')'; - } else $where = ''; - - $rows = $this->db->pq('SELECT s.subcomponentid, s.name, s.description, string_agg(hs.beamlinename) as beamlines FROM bf_subcomponent s INNER JOIN bf_subcomponent_beamline hs ON s.subcomponentid = hs.subcomponentid WHERE s.componentid=:1'.$where.' GROUP BY s.subcomponentid, s.name, s.description ORDER BY s.name', $args); - - $scom = array(); - foreach ($rows as $s) $scom[$s['SUBCOMPONENTID']] = $s['NAME']; - - $this->_output($this->has_arg('array') ? $scom : $rows); + else + $where = ''; + + $rows = $this->db->pq('SELECT c.componentid, c.name, c.description, string_agg(hc.beamlinename) as beamlines FROM bf_component c INNER JOIN bf_component_beamline hc ON c.componentid = hc.componentid WHERE c.systemid=:1' . $where . ' GROUP BY c.componentid, c.name, c.description ORDER BY beamlines,c.name', $args); + + $com = array(); + foreach ($rows as $c) + $com[$c['COMPONENTID']] = $c['NAME']; + + $this->_output($this->has_arg('array') ? $com : $rows); + } + + # ------------------------------------------------------------------------ + # Return a list of subcomponents for a component on a beamline + function _get_subcomponents() + { + if (!$this->has_arg('cid')) + $this->_error('No componentid specified'); + $args = array($this->arg('cid')); + + if ($this->has_arg('bl')) + { + $bls = $this->arg('bl') == 'P01' ? array('i02', 'i03', 'i04') : array($this->arg('bl')); + + $blw = array(); + foreach ($bls as $b) + { + array_push($blw, 'hs.beamlinename LIKE :' . (sizeof($args) + 1)); + array_push($args, $b); + } + + $where = ' AND (' . implode($blw, ' OR ') . ')'; } - + else + $where = ''; + + $rows = $this->db->pq('SELECT s.subcomponentid, s.name, s.description, string_agg(hs.beamlinename) as beamlines FROM bf_subcomponent s INNER JOIN bf_subcomponent_beamline hs ON s.subcomponentid = hs.subcomponentid WHERE s.componentid=:1' . $where . ' GROUP BY s.subcomponentid, s.name, s.description ORDER BY s.name', $args); - # ------------------------------------------------------------------------ - # Add a new system - function _add_system() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); + $scom = array(); + foreach ($rows as $s) + $scom[$s['SUBCOMPONENTID']] = $s['NAME']; - if (!$this->has_arg('NAME')) $this->_error('Please specify a system name'); - - $this->db->pq('INSERT INTO bf_system (systemid,name,description) - VALUES (s_bf_system.nextval, :1, :2) RETURNING systemid INTO :id', + $this->_output($this->has_arg('array') ? $scom : $rows); + } + + + # ------------------------------------------------------------------------ + # Add a new system + function _add_system() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('NAME')) + $this->_error('Please specify a system name'); + + $this->db->pq('INSERT INTO bf_system (systemid,name,description) + VALUES (s_bf_system.nextval, :1, :2) RETURNING systemid INTO :id', array($this->arg('NAME'), $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : '')); - - $sysid = $this->db->id(); - - if ($this->has_arg('BLS')) { - foreach ($this->arg('BLS') as $b) { - $this->db->pq('INSERT INTO bf_system_beamline (system_beamlineid, systemid, beamlinename) + + $sysid = $this->db->id(); + + if ($this->has_arg('BLS')) + { + foreach ($this->arg('BLS') as $b) + { + $this->db->pq('INSERT INTO bf_system_beamline (system_beamlineid, systemid, beamlinename) VALUES (s_bf_system_beamline.nextval,:1, :2)', array($sysid, $b)); - } } - - $this->_output(array('SYSTEMID' => $sysid)); } - - # ------------------------------------------------------------------------ - # Add a new component - function _add_component() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); - - if (!$this->has_arg('NAME')) $this->_error('Please specify a component name'); - if (!$this->has_arg('SYSTEMID')) $this->_error('Please specify a system id'); - - $this->db->pq('INSERT INTO bf_component (componentid, systemid,name,description) - VALUES (s_bf_component.nextval, :1, :2, :3) RETURNING componentid INTO :id', + + $this->_output(array('SYSTEMID' => $sysid)); + } + + # ------------------------------------------------------------------------ + # Add a new component + function _add_component() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('NAME')) + $this->_error('Please specify a component name'); + if (!$this->has_arg('SYSTEMID')) + $this->_error('Please specify a system id'); + + $this->db->pq('INSERT INTO bf_component (componentid, systemid,name,description) + VALUES (s_bf_component.nextval, :1, :2, :3) RETURNING componentid INTO :id', array($this->arg('SYSTEMID'), $this->arg('NAME'), $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : '')); - - $comid = $this->db->id(); - if ($this->has_arg('BLS')) { - foreach ($this->arg('BLS') as $b) { - $this->db->pq('INSERT INTO bf_component_beamline (component_beamlineid, componentid, beamlinename) - VALUES (s_bf_component_beamline.nextval,:1, :2)', + $comid = $this->db->id(); + + if ($this->has_arg('BLS')) + { + foreach ($this->arg('BLS') as $b) + { + $this->db->pq('INSERT INTO bf_component_beamline (component_beamlineid, componentid, beamlinename) + VALUES (s_bf_component_beamline.nextval,:1, :2)', array($comid, $b)); - } } - - $this->_output(array('COMPONENTID' => $comid)); } - - # ------------------------------------------------------------------------ - # Add a new subcomponent - function _add_subcomponent() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); - - if (!$this->has_arg('NAME')) $this->_error('Please specify a subcomponent name'); - if (!$this->has_arg('COMPONENTID')) $this->_error('Please specify a component id'); - - $this->db->pq('INSERT INTO bf_subcomponent (subcomponentid, componentid,name,description) - VALUES (s_bf_subcomponent.nextval, :1, :2, :3) RETURNING subcomponentid INTO :id', + + $this->_output(array('COMPONENTID' => $comid)); + } + + # ------------------------------------------------------------------------ + # Add a new subcomponent + function _add_subcomponent() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('NAME')) + $this->_error('Please specify a subcomponent name'); + if (!$this->has_arg('COMPONENTID')) + $this->_error('Please specify a component id'); + + $this->db->pq('INSERT INTO bf_subcomponent (subcomponentid, componentid,name,description) + VALUES (s_bf_subcomponent.nextval, :1, :2, :3) RETURNING subcomponentid INTO :id', array($this->arg('COMPONENTID'), $this->arg('NAME'), $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : '')); - - $scomid = $this->db->id(); - - if ($this->has_arg('BLS')) { - foreach ($this->arg('BLS') as $b) { - $this->db->pq('INSERT INTO bf_subcomponent_beamline (subcomponent_beamlineid, subcomponentid, beamlinename) - VALUES (s_bf_subcomponent_beamline.nextval,:1, :2)', + + $scomid = $this->db->id(); + + if ($this->has_arg('BLS')) + { + foreach ($this->arg('BLS') as $b) + { + $this->db->pq('INSERT INTO bf_subcomponent_beamline (subcomponent_beamlineid, subcomponentid, beamlinename) + VALUES (s_bf_subcomponent_beamline.nextval,:1, :2)', array($scomid, $b)); - } } - - $this->_output(array('SUBCOMPONENTID' => $scomid)); - } - - - # ------------------------------------------------------------------------ - # Delete a row - function _delete_component() { - $this->_output($_POST); - } + $this->_output(array('SUBCOMPONENTID' => $scomid)); + } - # ------------------------------------------------------------------------ - # Edit a row - function _update_system() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); - if (!$this->has_arg('SYSTEMID')) $this->_error('No system specified'); - $this->_edit($this->arg('SYSTEMID'), 'systems'); - } + # ------------------------------------------------------------------------ + # Delete a row + function _delete_component() + { + $this->_output($_POST); - function _update_component() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); + } - if (!$this->has_arg('COMPONENTID')) $this->_error('No component specified'); - $this->_edit($this->arg('COMPONENTID'), 'components'); - } - function _update_subcomponent() { - if (!$this->user->has('fault_admin')) $this->_error('No Access'); + # ------------------------------------------------------------------------ + # Edit a row + function _update_system() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('SYSTEMID')) + $this->_error('No system specified'); + $this->_edit($this->arg('SYSTEMID'), 'systems'); + } + + function _update_component() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('COMPONENTID')) + $this->_error('No component specified'); + $this->_edit($this->arg('COMPONENTID'), 'components'); + } + + function _update_subcomponent() + { + if (!$this->user->hasPermission('fault_admin')) + $this->_error('No Access'); + + if (!$this->has_arg('SUBCOMPONENTID')) + $this->_error('No subcomponent specified'); + $this->_edit($this->arg('SUBCOMPONENTID'), 'subcomponents'); + } + + + function _edit($id, $typ) + { + if (!$this->has_arg('NAME')) + $this->_error('No name specified'); + + $types = array('systems' => array('system'), 'components' => array('component'), 'subcomponents' => array('subcomponent')); + if (!array_key_exists($typ, $types)) + $this->_error('That type doesnt exists'); + + $ty = $types[$typ]; + + $check = $this->db->pq('SELECT ' . $ty[0] . 'id as id FROM bf_' . $ty[0] . ' WHERE ' . $ty[0] . 'id=:1', array($id)); + if (!sizeof($check)) + $this->_error("That $typ doesnt exist"); + + $desc = $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : ''; - if (!$this->has_arg('SUBCOMPONENTID')) $this->_error('No subcomponent specified'); - $this->_edit($this->arg('SUBCOMPONENTID'), 'subcomponents'); + $bls = $this->has_arg('BLS') ? $this->arg('BLS') : array(); + $bl_temp = $this->db->pq('SELECT ' . $ty[0] . '_beamlineid as id,beamlinename FROM bf_' . $ty[0] . '_beamline WHERE ' . $ty[0] . 'id=:1', array($id)); + $bl_old = array(); + foreach ($bl_temp as $b) + $bl_old[$b['BEAMLINENAME']] = $b['ID']; + + $rem = array_values(array_diff(array_keys($bl_old), $bls)); + $add = array_values(array_diff($bls, array_keys($bl_old))); + + + foreach ($rem as $r) + { + $this->db->pq('DELETE FROM bf_' . $ty[0] . '_beamline WHERE ' . $ty[0] . '_beamlineid=:1', array($bl_old[$r])); } + foreach ($add as $a) + { + $this->db->pq('INSERT INTO bf_' . $ty[0] . '_beamline (' . $ty[0] . '_beamlineid, ' . $ty[0] . 'id, beamlinename) + VALUES (s_bf_' . $ty[0] . '_beamline.nextval, :1, :2)', array($id, $a)); + } - function _edit($id, $typ) { - if (!$this->has_arg('NAME')) $this->_error('No name specified'); - - $types = array('systems' => array('system'), 'components' => array('component'), 'subcomponents' => array('subcomponent')); - if (!array_key_exists($typ, $types)) $this->_error('That type doesnt exists'); - - $ty = $types[$typ]; - - $check = $this->db->pq('SELECT '.$ty[0].'id as id FROM bf_'.$ty[0].' WHERE '.$ty[0].'id=:1', array($id)); - if (!sizeof($check)) $this->_error("That $typ doesnt exist"); - - $desc = $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : ''; - - $bls = $this->has_arg('BLS') ? $this->arg('BLS') : array(); - $bl_temp = $this->db->pq('SELECT '.$ty[0].'_beamlineid as id,beamlinename FROM bf_'.$ty[0].'_beamline WHERE '.$ty[0].'id=:1', array($id)); - $bl_old = array(); - foreach ($bl_temp as $b) $bl_old[$b['BEAMLINENAME']] = $b['ID']; - - $rem = array_values(array_diff(array_keys($bl_old), $bls)); - $add = array_values(array_diff($bls, array_keys($bl_old))); - - - foreach ($rem as $r) { - $this->db->pq('DELETE FROM bf_'.$ty[0].'_beamline WHERE '.$ty[0].'_beamlineid=:1', array($bl_old[$r])); - } - - foreach ($add as $a) { - $this->db->pq('INSERT INTO bf_'.$ty[0].'_beamline ('.$ty[0].'_beamlineid, '.$ty[0].'id, beamlinename) - VALUES (s_bf_'.$ty[0].'_beamline.nextval, :1, :2)', array($id, $a)); - } - - $this->db->pq('UPDATE bf_'.$ty[0].' SET name=:1, description=:2 WHERE '.$ty[0].'id=:3', array($this->arg('NAME'), $desc, $id)); - - $this->_output(1); + $this->db->pq('UPDATE bf_' . $ty[0] . ' SET name=:1, description=:2 WHERE ' . $ty[0] . 'id=:3', array($this->arg('NAME'), $desc, $id)); + + $this->_output(1); + } + + + # ------------------------------------------------------------------------ + # Add fault via ajax + function _add_fault() + { + if (!$this->user->hasPermission('fault_add')) + $this->_error('No Access'); + + $valid = True; + foreach (array('TITLE', 'DESCRIPTION', 'SESSIONID', 'STARTTIME', 'SUBCOMPONENTID', 'BEAMTIMELOST', 'RESOLVED') as $f) + { + if (!$this->has_arg($f)) + $valid = False; } - - # ------------------------------------------------------------------------ - # Add fault via ajax - function _add_fault() { - if (!$this->user->has('fault_add')) $this->_error('No Access'); + if (!$valid) + $this->_error('Missing Fields'); - $valid = True; - foreach (array('TITLE', 'DESCRIPTION', 'SESSIONID', 'STARTTIME', 'SUBCOMPONENTID', 'BEAMTIMELOST', 'RESOLVED') as $f) { - if (!$this->has_arg($f)) $valid = False; - } - - if (!$valid) $this->_error('Missing Fields'); - - $btlstart = $this->has_arg('BEAMTIMELOST_STARTTIME') ? $this->arg('BEAMTIMELOST_STARTTIME') : ''; - $btlend = $this->has_arg('BEAMTIMELOST_ENDTIME') ? $this->arg('BEAMTIMELOST_ENDTIME') : ''; - $end = $this->has_arg('ENDTIME') ? $this->arg('ENDTIME') : ''; - $as = $this->has_arg('ASSIGNEEID') ? $this->arg('ASSIGNEEID') : null; - $res = $this->has_arg('RESOLUTION') ? $this->arg('RESOLUTION') : ''; - - $this->db->pq("INSERT INTO bf_fault (faultid, sessionid, personid, subcomponentid, starttime, endtime, beamtimelost, beamtimelost_starttime, beamtimelost_endtime, title, description, resolved, resolution, assigneeid) - VALUES (s_bf_fault.nextval, :1, :2, :3, TO_DATE(:4, 'DD-MM-YYYY HH24:MI'), TO_DATE(:5, 'DD-MM-YYYY HH24:MI'), :6, TO_DATE(:7, 'DD-MM-YYYY HH24:MI'), TO_DATE(:8, 'DD-MM-YYYY HH24:MI'), :9, :10, :11, :12, :13) RETURNING faultid INTO :id", - array($this->arg('SESSIONID'), $this->user->personid, $this->arg('SUBCOMPONENTID'), $this->arg('STARTTIME'), $end, $this->arg('BEAMTIMELOST'), $btlstart, $btlend, $this->arg('TITLE'), $this->arg('DESCRIPTION'), $this->arg('RESOLVED'), $res, $as)); - - $newid = $this->db->id(); - - $info = $this->db->pq("SELECT CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as name, CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber),'-'),bl.visit_number) as visit, bl.beamlinename as beamline, s.name as system, c.name as component, sc.name as subcomponent, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI') as starttime, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI') as endtime, f.beamtimelost, round(TIMESTAMPDIFF('MINUTE', f.beamtimelost_starttime, f.beamtimelost_endtime)/60,2) as lost, f.title, f.resolved, f.resolution, f.description, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI') as beamtimelost_endtime, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI') as beamtimelost_starttime, f.owner + $btlstart = $this->has_arg('BEAMTIMELOST_STARTTIME') ? $this->arg('BEAMTIMELOST_STARTTIME') : ''; + $btlend = $this->has_arg('BEAMTIMELOST_ENDTIME') ? $this->arg('BEAMTIMELOST_ENDTIME') : ''; + $end = $this->has_arg('ENDTIME') ? $this->arg('ENDTIME') : ''; + $as = $this->has_arg('ASSIGNEEID') ? $this->arg('ASSIGNEEID') : null; + $res = $this->has_arg('RESOLUTION') ? $this->arg('RESOLUTION') : ''; + + $this->db->pq("INSERT INTO bf_fault (faultid, sessionid, personid, subcomponentid, starttime, endtime, beamtimelost, beamtimelost_starttime, beamtimelost_endtime, title, description, resolved, resolution, assigneeid) + VALUES (s_bf_fault.nextval, :1, :2, :3, TO_DATE(:4, 'DD-MM-YYYY HH24:MI'), TO_DATE(:5, 'DD-MM-YYYY HH24:MI'), :6, TO_DATE(:7, 'DD-MM-YYYY HH24:MI'), TO_DATE(:8, 'DD-MM-YYYY HH24:MI'), :9, :10, :11, :12, :13) RETURNING faultid INTO :id", + array($this->arg('SESSIONID'), $this->user->personId, $this->arg('SUBCOMPONENTID'), $this->arg('STARTTIME'), $end, $this->arg('BEAMTIMELOST'), $btlstart, $btlend, $this->arg('TITLE'), $this->arg('DESCRIPTION'), $this->arg('RESOLVED'), $res, $as)); + + $newid = $this->db->id(); + + $info = $this->db->pq("SELECT CONCAT(pe.givenname, ' ', pe.familyname) as name, CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) as visit, bl.beamlinename as beamline, s.name as system, c.name as component, sc.name as subcomponent, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI') as starttime, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI') as endtime, f.beamtimelost, round(TIMESTAMPDIFF('MINUTE', f.beamtimelost_starttime, f.beamtimelost_endtime)/60,2) as lost, f.title, f.resolved, f.resolution, f.description, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI') as beamtimelost_endtime, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI') as beamtimelost_starttime, f.owner FROM bf_fault f INNER JOIN bf_subcomponent sc ON f.subcomponentid = sc.subcomponentid INNER JOIN bf_component c ON sc.componentid = c.componentid @@ -529,53 +638,58 @@ function _add_fault() { INNER JOIN person pe ON pe.personid = f.personid WHERE f.faultid=:1", array($newid)); - - $info = $info[0]; - - foreach (array('DESCRIPTION', 'RESOLUTION') as $k) { - #if ($info[$k]) { - #$info[$k] = Markdown::defaultTransform($info[$k]->read($info[$k]->size())); - $info[$k] = $this->db->read($info[$k]); - #} - } - - $report = ''.$info['TITLE'].'

Reported By: '.$info['NAME'].'

System: '.$info['SYSTEM'].'
Component: '.$info['COMPONENT'].' » '.$info['SUBCOMPONENT'].'

Start: '.$info['STARTTIME'].' End: '.($info['RESOLVED'] == 1 ? $info['ENDTIME'] : 'N/A') .'
Resolved: '.($info['RESOLVED'] == 2 ? 'Partial' : ($info['RESOLVED'] ? 'Yes' : 'No')).'
Beamtime Lost: '.($info['BEAMTIMELOST'] ? ('Yes ('.$info['LOST'].'h between '.$info['BEAMTIMELOST_STARTTIME'].' and '.$info['BEAMTIMELOST_ENDTIME'].')') : 'No').'

Description
'.$info['DESCRIPTION'].'

'.($info['RESOLVED'] ? ('Resolution
'.$info['RESOLUTION']):'').'

Fault Report Link'; - - $data = array('txtTITLE' => 'Fault Report: '. $info['TITLE'], - 'txtCONTENT' => $report, - 'txtLOGBOOKID' =>'BL'.strtoupper($info['BEAMLINE']), - 'txtGROUPID' => 'GEN', - 'txtENTRYTYPEID'=> '41', - 'txtUSERID' => $this->user->login, - 'txtMANUALAUTO' => 'M', - ); - - - if (array_key_exists('userfile1', $_FILES)) { - if ($_FILES['userfile1']['name']) { - move_uploaded_file($_FILES['userfile1']['tmp_name'], '/tmp/fault_'.strtolower($_FILES['userfile1']['name'])); - $data['userfile1'] = '@/tmp/fault_'.strtolower($_FILES['userfile1']['name']); - } - } - - $ch = curl_init('http://rdb.pri.diamond.ac.uk/php/elog/cs_logentryext_bl.php'); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - $response = curl_exec($ch); - curl_close($ch); - - - if (preg_match('/New Log Entry ID:(\d+)/', $response, $eid)) { - $this->db->pq('UPDATE bf_fault SET elogid=:1 WHERE faultid=:2', array($eid[1], $newid)); - } - - if (preg_match('/Attachment Id:(\d+)/', $response, $aid)) { - $this->db->pq('UPDATE bf_fault SET attachment=:1 WHERE faultid=:2', array($aid[1].'-fault_'.strtolower($_FILES['userfile1']['name']), $newid)); + + $info = $info[0]; + + foreach (array('DESCRIPTION', 'RESOLUTION') as $k) + { + #if ($info[$k]) { + #$info[$k] = Markdown::defaultTransform($info[$k]->read($info[$k]->size())); + $info[$k] = $this->db->read($info[$k]); + #} + } + + $report = '' . $info['TITLE'] . '

Reported By: ' . $info['NAME'] . '

System: ' . $info['SYSTEM'] . '
Component: ' . $info['COMPONENT'] . ' » ' . $info['SUBCOMPONENT'] . '

Start: ' . $info['STARTTIME'] . ' End: ' . ($info['RESOLVED'] == 1 ? $info['ENDTIME'] : 'N/A') . '
Resolved: ' . ($info['RESOLVED'] == 2 ? 'Partial' : ($info['RESOLVED'] ? 'Yes' : 'No')) . '
Beamtime Lost: ' . ($info['BEAMTIMELOST'] ? ('Yes (' . $info['LOST'] . 'h between ' . $info['BEAMTIMELOST_STARTTIME'] . ' and ' . $info['BEAMTIMELOST_ENDTIME'] . ')') : 'No') . '

Description
' . $info['DESCRIPTION'] . '

' . ($info['RESOLVED'] ? ('Resolution
' . $info['RESOLUTION']) : '') . '

Fault Report Link'; + + $data = array('txtTITLE' => 'Fault Report: ' . $info['TITLE'], + 'txtCONTENT' => $report, + 'txtLOGBOOKID' => 'BL' . strtoupper($info['BEAMLINE']), + 'txtGROUPID' => 'GEN', + 'txtENTRYTYPEID' => '41', + 'txtUSERID' => $this->user->loginId, + 'txtMANUALAUTO' => 'M', + ); + + + if (array_key_exists('userfile1', $_FILES)) + { + if ($_FILES['userfile1']['name']) + { + move_uploaded_file($_FILES['userfile1']['tmp_name'], '/tmp/fault_' . strtolower($_FILES['userfile1']['name'])); + $data['userfile1'] = '@/tmp/fault_' . strtolower($_FILES['userfile1']['name']); } + } + + $ch = curl_init('http://rdb.pri.diamond.ac.uk/php/elog/cs_logentryext_bl.php'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + curl_close($ch); + - - $this->_output(array('FAULTID' => $newid)); + if (preg_match('/New Log Entry ID:(\d+)/', $response, $eid)) + { + $this->db->pq('UPDATE bf_fault SET elogid=:1 WHERE faultid=:2', array($eid[1], $newid)); } + + if (preg_match('/Attachment Id:(\d+)/', $response, $aid)) + { + $this->db->pq('UPDATE bf_fault SET attachment=:1 WHERE faultid=:2', array($aid[1] . '-fault_' . strtolower($_FILES['userfile1']['name']), $newid)); + } + + + $this->_output(array('FAULTID' => $newid)); + } } diff --git a/api/src/Page/Image.php b/api/src/Page/Image.php index 28a27b711..56925ae6d 100644 --- a/api/src/Page/Image.php +++ b/api/src/Page/Image.php @@ -14,7 +14,7 @@ class Image extends Page 'id' => '\d+', 'n' => '\d+', 'f' => '\d', - 'bl' => '[\w-]+', + 'bl' => '[\w\-]+', 'w' => '\d+', 'fid' => '\d+', 'aid' => '\d+', @@ -119,7 +119,7 @@ function _action_image() { FROM robotaction r INNER JOIN blsession s ON r.blsessionid = s.sessionid INNER JOIN proposal p ON s.proposalid = p.proposalid - WHERE r.robotactionid=:1 AND CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :2", array($this->arg('aid'), $this->arg('visit'))); + WHERE r.robotactionid=:1 AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :2", array($this->arg('aid'), $this->arg('visit'))); //print_r($image); @@ -191,24 +191,51 @@ function _diffraction_viewer() { if ($n > $info['NUM']) { $this->_error('Not found', 'That image does not exist'); } - + $im = $info['LOC'] . '/' . $info['FT']; - $out = '/tmp/'.$this->arg('id').'_'.$n.($this->has_arg('thresh')?'_th':'').'.jpg'; - - if (!file_exists($out)) { - $resp = $this->_curl(array( - 'url' => 'http://localhost:5000/dc/image', - 'jwt' => true, - 'data' => array( - 'dcid' => $this->arg('id'), - 'image' => $n, - 'binning' => 4, - 'threshold' => $this->has_arg('thresh') ? 1 : 0, - ) - )); + $out = '/tmp/' . $this->arg('id') . '_' . $n . ($this->has_arg('thresh') ? '_th' : '') . '.jpg'; + global $dials_rest_url, $dials_rest_jwt; + if (!file_exists($out)) { + if (!empty($dials_rest_url) && !empty($dials_rest_jwt)) { + $resp = $this->_curl(array( + 'url' => $dials_rest_url.'/export_bitmap/', + 'HEADERS' => array( + 'Content-Type: application/json', + 'accept: application/json', + 'Authorization: Bearer ' . $dials_rest_jwt, + ), + 'POST' => 1, + 'FIELDS' => array( + 'filename' => $im, + 'image_index' => $n, + 'binning' => 4, + 'display' => $this->has_arg('thresh') ? "threshold" : "image", + 'colour_scheme' => 'greyscale', + 'brightness' => $this->has_arg('thresh') ? 1000 : 10, + 'format' => 'png', + ) + )); + } else { + $resp = $this->_curl(array( + 'url' => 'http://localhost:5000/dc/image', + 'jwt' => true, + 'data' => array( + 'dcid' => $this->arg('id'), + 'image' => $n, + 'binning' => 4, + 'threshold' => $this->has_arg('thresh') ? 1 : 0, + ) + )); + } if ($resp['code'] == 200) { file_put_contents($out, $resp['content']); + } else if ($resp['code'] == 403) { + error_log("Not authorised when asking for grid scan image. Has the JWT token expired?". PHP_EOL); + $this->_error('Unauthroised in image service', 'Image server has had an error.'); + } else { + error_log("Gridscan failed to contact external service. " . $resp['code'] . " - " . $resp['content'] . PHP_EOL); + $this->_error('Not found', 'Image not provided by image service.'); } } @@ -219,6 +246,7 @@ function _diffraction_viewer() { $this->app->response->headers->set("Content-length", $size); readfile($out); } else { + error_log("Grid scan image file no longer exists and should do."); $this->_error('Not found', 'That image is no longer available'); } } diff --git a/api/src/Page/Imaging.php b/api/src/Page/Imaging.php index dc051287f..db495ba96 100644 --- a/api/src/Page/Imaging.php +++ b/api/src/Page/Imaging.php @@ -7,231 +7,248 @@ class Imaging extends Page { + public static $arg_list = array( + 'cid' => '\d+', + 'iid' => '\d+', + 'imid' => '\d+', + 'itid' => '\d+', + 'igid' => '\d+', + 'shid' => '\d+', + 'shcid' => '\d+', + 'scid' => '\d+', + 'sccid' => '\d+', + 'scgid' => '\d+', + 'sid' => '\d+', + 'f' => '\d', + 'did' => '\d+', + 'all' => '\d', + 'allStates' => '\d', + 'delta' => '\d', + + 'SCHEDULEID' => '\d+', + 'SCHEULECOMPONENTID' => '\d+', + 'CONTAINERID' => '\d+', + 'CONTAINERINSPECTIONID' => '\d+', + 'IMAGERID' => '\d+', + 'TEMPERATURE' => '\d+', + 'INSPECTIONTYPEID' => '\d+', + 'BLSAMPLEID' => '\d+', + 'BLSAMPLEIMAGESCOREID' => '\d+', + 'COMMENTS' => '.*', + 'NAME' => '[\w|\s|\-]+', + 'MANUAL' => '\d', + 'OFFSET_HOURS' => '\d+', + 'BLTIMESTAMP' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + + 'POSITION' => '\d+', + 'SCREENID' => '\d+', + 'PROPOSALID' => '\d+', + 'COMPONENTID' => '\d+', + 'GLOBAL' => '\d', + 'SCREENCOMPONENTGROUPID' => '\d+', + 'SCREENCOMPONENTID' => '\d+', + 'CONCENTRATION' => '\d+(.\d+)?', + 'PH' => '\d+(.\d+)?', + + 'BLSAMPLEIMAGEAUTOSCORESCHEMAID' => '\d', + ); + + + public static $dispatch = array( + array('/inspection(/:iid)', 'get', '_get_inspections'), + array('/inspection/locations/:barcode', 'get', '_get_locations'), + array('/inspection', 'post', '_add_inspection'), + + array('/inspection/images(/:imid)(/iid/:iid)', 'get', '_get_inspection_images'), + array('/inspection/images', 'post', '_add_inspection_image'), + array('/inspection/images/:imid', 'patch', '_update_inspection_image'), + + array('/inspection/images/scores', 'get', '_get_image_scores'), + array('/inspection/images/scores/auto', 'get', '_get_auto_scores'), + array('/inspection/images/scores/auto/schemas', 'get', '_get_auto_score_schemas'), + + array('/inspection/image/:imid', 'get', '_get_image'), + + array('/inspection/types(/:itid)', 'get', '_get_inspection_types'), + + array('/inspection/adhoc', 'get', '_add_adhoc_inspection'), + + + array('/imager(/:igid)', 'get', '_get_imagers'), + + + array('/schedule(/:shid)', 'get', '_get_schedules'), + array('/schedule/:shid', 'patch', '_update_schedule'), + array('/schedule', 'post', '_add_schedule'), + + + array('/schedule/components(/:shcid)(/shid/:shid)', 'get', '_get_schedule_components'), + array('/schedule/components', 'post', '_add_schedule_component'), + array('/schedule/components/:shcid', 'put', '_update_schedule_component'), + + + + array('/screen(/:scid)', 'get', '_get_screens'), + array('/screen', 'post', '_add_screen'), + array('/screen/:scid', 'patch', '_update_screen'), + + array('/screen/groups(/:scgid)', 'get', '_get_screen_componentgroups'), + array('/screen/groups', 'post', '_add_screen_componentgroup'), + array('/screen/groups', 'put', '_add_screen_componentgroups'), + array('/screen/groups/:scgid', 'patch', '_update_screen_componentgroup'), + + array('/screen/components(/:sccid)', 'get', '_get_screen_components'), + array('/screen/components', 'post', '_add_screen_component'), + array('/screen/components', 'put', '_add_screen_components'), + array('/screen/components/:sccid', 'patch', '_update_screen_component'), + array('/screen/components/:sccid', 'put', '_update_screen_component_full'), + array('/screen/components/:sccid', 'delete', '_delete_screen_component'), + ); + + var $shared; + + # Initialise shared functions + function __construct() + { + call_user_func_array(array('parent', '__construct'), func_get_args()); + + $this->shared = $this->app->container['imagingShared']; + } - public static $arg_list = array('cid' => '\d+', - 'iid' => '\d+', - 'imid' => '\d+', - 'itid' => '\d+', - 'igid' => '\d+', - 'shid' => '\d+', - 'shcid' => '\d+', - 'scid' => '\d+', - 'sccid' => '\d+', - 'scgid' => '\d+', - 'sid' => '\d+', - 'f' => '\d', - 'did' => '\d+', - 'all' => '\d', - 'allStates' => '\d', - 'delta' => '\d', - - 'SCHEDULEID' => '\d+', - 'SCHEULECOMPONENTID' => '\d+', - 'CONTAINERID' => '\d+', - 'CONTAINERINSPECTIONID' => '\d+', - 'IMAGERID' => '\d+', - 'TEMPERATURE' => '\d+', - 'INSPECTIONTYPEID' => '\d+', - 'BLSAMPLEID' => '\d+', - 'BLSAMPLEIMAGESCOREID' => '\d+', - 'COMMENTS' => '.*', - 'NAME' => '[\w|\s|-]+', - 'MANUAL' => '\d', - 'OFFSET_HOURS' => '\d+', - 'BLTIMESTAMP' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - - 'POSITION' => '\d+', - 'SCREENID' => '\d+', - 'PROPOSALID' => '\d+', - 'COMPONENTID' => '\d+', - 'GLOBAL' => '\d', - 'SCREENCOMPONENTGROUPID' => '\d+', - 'SCREENCOMPONENTID' => '\d+', - 'CONCENTRATION' => '\d+(.\d+)?', - 'PH' => '\d+(.\d+)?', - - 'BLSAMPLEIMAGEAUTOSCORESCHEMAID' => '\d', - ); - - - public static $dispatch = array(array('/inspection(/:iid)', 'get', '_get_inspections'), - array('/inspection/locations/:barcode', 'get', '_get_locations'), - array('/inspection', 'post', '_add_inspection'), - - array('/inspection/images(/:imid)(/iid/:iid)', 'get', '_get_inspection_images'), - array('/inspection/images', 'post', '_add_inspection_image'), - array('/inspection/images/:imid', 'patch', '_update_inspection_image'), - - array('/inspection/images/scores', 'get', '_get_image_scores'), - array('/inspection/images/scores/auto', 'get', '_get_auto_scores'), - array('/inspection/images/scores/auto/schemas', 'get', '_get_auto_score_schemas'), - - array('/inspection/image/:imid', 'get', '_get_image'), - - array('/inspection/types(/:itid)', 'get', '_get_inspection_types'), - - array('/inspection/adhoc', 'get', '_add_adhoc_inspection'), - - - array('/imager(/:igid)', 'get', '_get_imagers'), - - - array('/schedule(/:shid)', 'get', '_get_schedules'), - array('/schedule/:shid', 'patch', '_update_schedule'), - array('/schedule', 'post', '_add_schedule'), - - - array('/schedule/components(/:shcid)(/shid/:shid)', 'get', '_get_schedule_components'), - array('/schedule/components', 'post', '_add_schedule_component'), - array('/schedule/components/:shcid', 'put', '_update_schedule_component'), - - - - array('/screen(/:scid)', 'get', '_get_screens'), - array('/screen', 'post', '_add_screen'), - array('/screen/:scid', 'patch', '_update_screen'), - - array('/screen/groups(/:scgid)', 'get', '_get_screen_componentgroups'), - array('/screen/groups', 'post', '_add_screen_componentgroup'), - array('/screen/groups', 'put', '_add_screen_componentgroups'), - array('/screen/groups/:scgid', 'patch', '_update_screen_componentgroup'), - - array('/screen/components(/:sccid)', 'get', '_get_screen_components'), - array('/screen/components', 'post', '_add_screen_component'), - array('/screen/components', 'put', '_add_screen_components'), - array('/screen/components/:sccid', 'patch', '_update_screen_component'), - array('/screen/components/:sccid', 'put', '_update_screen_component_full'), - array('/screen/components/:sccid', 'delete', '_delete_screen_component'), - ); - - # Initialise shared functions - function __construct() { - call_user_func_array(array('parent', '__construct'), func_get_args()); - - $this->shared = new ImagingShared($this->db); - } - + # Returns a list of drop numbers for a plate identified by its unique barcode + # For use on Formulatrix via ImagerLink + function _get_locations() + { + $args = array(); + array_push($args, $this->arg('barcode')); - # Returns a list of drop numbers for a plate identified by its unique barcode - # For use on Formulatrix via ImagerLink - function _get_locations(){ - $args = array(); - array_push($args, $this->arg('barcode')); - - $locations = $this->db->pq("SELECT location FROM BLSample s INNER JOIN Container c ON s.containerId = c.containerId WHERE barcode=:1", $args); + $locations = $this->db->pq("SELECT location FROM BLSample s INNER JOIN Container c ON s.containerId = c.containerId WHERE barcode=:1", $args); - if(sizeof($locations)) - $this->_output($locations); - else - $this->_error('Barcode not found, or no valid locations for container', 404); - } + if (sizeof($locations)) + $this->_output($locations); + else + $this->_error('Barcode not found, or no valid locations for container', 404); + } - function _get_imagers() { - $where = '1=1'; - $args = array(); + function _get_imagers() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('igid')) { - $where .= ' AND i.imagerid=:1'; - array_push($args, $this->arg('igid')); - } + if ($this->has_arg('igid')) { + $where .= ' AND i.imagerid=:1'; + array_push($args, $this->arg('igid')); + } - $imagers = $this->db->pq("SELECT i.capacity, i.imagerid, i.name, i.temperature, i.serial, count(c.containerid) as containers, ROUND(COUNT(c.containerid)/i.capacity*100,1) as pusage + $imagers = $this->db->pq("SELECT i.capacity, i.imagerid, i.name, i.temperature, i.serial, count(c.containerid) as containers, ROUND(COUNT(c.containerid)/i.capacity*100,1) as pusage FROM imager i LEFT OUTER JOIN container c ON c.imagerid = i.imagerid WHERE $where GROUP BY i.capacity, i.imagerid, i.name, i.temperature, i.serial ", $args); - if ($this->has_arg('igid')) { - if (sizeof($imagers)) $this->_output($imagers[0]); - else $this->_error('No such imager'); - } else $this->_output($imagers); - } + if ($this->has_arg('igid')) { + if (sizeof($imagers)) + $this->_output($imagers[0]); + else + $this->_error('No such imager'); + } else + $this->_output($imagers); + } - function _get_schedules() { - $where = '1=1'; - $args = array(); + function _get_schedules() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('shid')) { - $where .= ' AND s.scheduleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('shid')); - } + if ($this->has_arg('shid')) { + $where .= ' AND s.scheduleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('shid')); + } - $schedules = $this->db->pq("SELECT s.scheduleid, s.name, count(distinct sc.schedulecomponentid) as schedulecomponents, count(distinct c.containerid) as containers + $schedules = $this->db->pq("SELECT s.scheduleid, s.name, count(distinct sc.schedulecomponentid) as schedulecomponents, count(distinct c.containerid) as containers FROM schedule s LEFT OUTER JOIN schedulecomponent sc ON sc.scheduleid = s.scheduleid LEFT OUTER JOIN container c ON c.scheduleid = s.scheduleid WHERE $where GROUP BY s.scheduleid, s.name", $args); - if ($this->has_arg('shid')) { - if (sizeof($schedules)) $this->_output($schedules[0]); - else $this->_error('No such schedule'); - - } else $this->_output($schedules); - } + if ($this->has_arg('shid')) { + if (sizeof($schedules)) + $this->_output($schedules[0]); + else + $this->_error('No such schedule'); + } else + $this->_output($schedules); + } - function _add_schedule() { - $this->user->can('schedules'); + function _add_schedule() + { + $this->haltIfLackingPermission('schedules'); - if (!$this->has_arg('NAME')) $this->_error('No name specified'); + if (!$this->has_arg('NAME')) + $this->_error('No name specified'); - $this->db->pq("INSERT INTO schedule (scheduleid, name) + $this->db->pq("INSERT INTO schedule (scheduleid, name) VALUES (s_schedule.nextval, :1) RETURNING scheduleid INTO :id", array($this->arg('NAME'))); - $this->_output(array('SCHEDULEID' => $this->db->id())); - } + $this->_output(array('SCHEDULEID' => $this->db->id())); + } - function _update_schedule() { - $this->user->can('schedules'); + function _update_schedule() + { + $this->haltIfLackingPermission('schedules'); - if (!$this->has_arg('shid')) $this->_error('No schedule specified'); + if (!$this->has_arg('shid')) + $this->_error('No schedule specified'); - $sch = $this->db->pq("SELECT scheduleid FROM schedule WHERE scheduleid=:1", array($this->arg('shid'))); - if (!sizeof($sch)) $this->_error('No such schedule'); + $sch = $this->db->pq("SELECT scheduleid FROM schedule WHERE scheduleid=:1", array($this->arg('shid'))); + if (!sizeof($sch)) + $this->_error('No such schedule'); - foreach(array('NAME') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE schedule SET '.$f.'=:1 WHERE scheduleid=:2', array($this->arg($f), $this->arg('shid'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('NAME') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE schedule SET ' . $f . '=:1 WHERE scheduleid=:2', array($this->arg($f), $this->arg('shid'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _get_schedule_components() { - $where = '1=1'; - $args = array(); - $join = ''; + function _get_schedule_components() + { + $where = '1=1'; + $args = array(); + $join = ''; - if ($this->has_arg('shid')) { - $where .= ' AND sc.scheduleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('shid')); - } + if ($this->has_arg('shid')) { + $where .= ' AND sc.scheduleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('shid')); + } - if ($this->has_arg('shcid')) { - $where .= ' AND sc.schedulecomponentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('shcid')); - } + if ($this->has_arg('shcid')) { + $where .= ' AND sc.schedulecomponentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('shcid')); + } - if ($this->has_arg('cid')) { - $where .= ' AND ci.containerid=:'.(sizeof($args)+1); - $join = 'LEFT OUTER JOIN containerinspection ci ON ci.schedulecomponentid = sc.schedulecomponentid'; - array_push($args, $this->arg('cid')); - } + if ($this->has_arg('cid')) { + $where .= ' AND ci.containerid=:' . (sizeof($args) + 1); + $join = 'LEFT OUTER JOIN containerinspection ci ON ci.schedulecomponentid = sc.schedulecomponentid'; + array_push($args, $this->arg('cid')); + } - $components = $this->db->pq("SELECT sc.schedulecomponentid, sc.inspectiontypeid, sc.scheduleid, sc.offset_hours, t.name as inspectiontype + $components = $this->db->pq("SELECT sc.schedulecomponentid, sc.inspectiontypeid, sc.scheduleid, sc.offset_hours, t.name as inspectiontype FROM schedulecomponent sc INNER JOIN inspectiontype t ON t.inspectiontypeid = sc.inspectiontypeid $join @@ -239,162 +256,191 @@ function _get_schedule_components() { ORDER BY sc.offset_hours", $args); - if ($this->has_arg('shcid')) { - if (sizeof($components)) $this->_output($components[0]); - else $this->_error('No such component'); - - } else $this->_output($components); - } + if ($this->has_arg('shcid')) { + if (sizeof($components)) + $this->_output($components[0]); + else + $this->_error('No such component'); + } else + $this->_output($components); + } - function _add_schedule_component() { - $this->user->can('schedule_comps'); + function _add_schedule_component() + { + $this->haltIfLackingPermission('schedule_comps'); - if (!$this->has_arg('SCHEDULEID')) $this->_error('No schedule specified'); - if (!$this->has_arg('OFFSET_HOURS')) $this->_error('No time offset specified'); - if (!$this->has_arg('INSPECTIONTYPEID')) $this->_error('No inspection type specified'); + if (!$this->has_arg('SCHEDULEID')) + $this->_error('No schedule specified'); + if (!$this->has_arg('OFFSET_HOURS')) + $this->_error('No time offset specified'); + if (!$this->has_arg('INSPECTIONTYPEID')) + $this->_error('No inspection type specified'); - $sch = $this->db->pq("SELECT scheduleid FROM schedule WHERE scheduleid=:1", array($this->arg('SCHEDULEID'))); - if (!sizeof($sch)) $this->_error('No such schedule'); + $sch = $this->db->pq("SELECT scheduleid FROM schedule WHERE scheduleid=:1", array($this->arg('SCHEDULEID'))); + if (!sizeof($sch)) + $this->_error('No such schedule'); - $this->db->pq("INSERT INTO schedulecomponent (schedulecomponentid, scheduleid, offset_hours, inspectiontypeid) + $this->db->pq("INSERT INTO schedulecomponent (schedulecomponentid, scheduleid, offset_hours, inspectiontypeid) VALUES (s_schedulecomponent.nextval, :1, :2, :3) RETURNING schedulecomponentid INTO :id", array($this->arg('SCHEDULEID'), $this->arg('OFFSET_HOURS'), $this->arg('INSPECTIONTYPEID'))); - $this->_output(array('SCHEDULECOMPONENTID' => $this->db->id())); - } + $this->_output(array('SCHEDULECOMPONENTID' => $this->db->id())); + } - function _update_schedule_component() { - $this->user->can('schedule_comps'); + function _update_schedule_component() + { + $this->haltIfLackingPermission('schedule_comps'); - if (!$this->has_arg('shcid')) $this->_error('No schedule component specified'); - if (!$this->has_arg('OFFSET_HOURS')) $this->_error('No offset specified'); - if (!$this->has_arg('INSPECTIONTYPEID')) $this->_error('No type specified'); + if (!$this->has_arg('shcid')) + $this->_error('No schedule component specified'); + if (!$this->has_arg('OFFSET_HOURS')) + $this->_error('No offset specified'); + if (!$this->has_arg('INSPECTIONTYPEID')) + $this->_error('No type specified'); - $sc = $this->db->pq("SELECT schedulecomponentid FROM schedulecomponent WHERE schedulecomponentid=:1", array($this->arg('shcid'))); - if (!sizeof($sc)) $this->_error('No such schedule component'); + $sc = $this->db->pq("SELECT schedulecomponentid FROM schedulecomponent WHERE schedulecomponentid=:1", array($this->arg('shcid'))); + if (!sizeof($sc)) + $this->_error('No such schedule component'); - $this->db->pq("UPDATE schedulecomponent SET offset_hours=:1, inspectiontypeid=:2 WHERE schedulecomponentid=:3", - array($this->arg('OFFSET_HOURS'), $this->arg('INSPECTIONTYPEID'), $this->arg('shcid'))); + $this->db->pq( + "UPDATE schedulecomponent SET offset_hours=:1, inspectiontypeid=:2 WHERE schedulecomponentid=:3", + array($this->arg('OFFSET_HOURS'), $this->arg('INSPECTIONTYPEID'), $this->arg('shcid')) + ); - $this->_output(array( - 'OFFSET_HOURS' => $this->arg('OFFSET_HOURS'), - 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), - )); - } + $this->_output(array( + 'OFFSET_HOURS' => $this->arg('OFFSET_HOURS'), + 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), + )); + } - function _get_inspection_types() { - $where = '1=1'; - $args = array(); + function _get_inspection_types() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('itid')) { - $where .= ' AND inspectiontypeid=:1'; - array_push($args, $this->arg('itid')); - } + if ($this->has_arg('itid')) { + $where .= ' AND inspectiontypeid=:1'; + array_push($args, $this->arg('itid')); + } - $types = $this->db->pq("SELECT inspectiontypeid, name FROM inspectiontype WHERE $where", $args); + $types = $this->db->pq("SELECT inspectiontypeid, name FROM inspectiontype WHERE $where", $args); - if ($this->has_arg('itid')) { - if (sizeof($types)) $this->_output($types[0]); - else $this->_error('No such inspection type'); - } else $this->_output($types); - } + if ($this->has_arg('itid')) { + if (sizeof($types)) + $this->_output($types[0]); + else + $this->_error('No such inspection type'); + } else + $this->_output($types); + } - # Get list of inspections for a container - function _get_inspections() { - global $img; + # Get list of inspections for a container + function _get_inspections() + { + global $img; - $join = ''; - $extc = ''; + $join = ''; + $extc = ''; - if (!$this->has_arg('cid') && - !$this->has_arg('iid') && - (!$this->staff || !$this->has_arg('all'))) $this->_error('No container / inspection specified'); + if ( + !$this->has_arg('cid') && + !$this->has_arg('iid') && + (!$this->staff || !$this->has_arg('all')) + ) + $this->_error('No container / inspection specified'); - if (($this->has_arg('all') && $this->staff) || in_array($_SERVER["REMOTE_ADDR"], $img)) { - $where = '1=1'; - $args = array(); - } else { - $where = 'p.proposalid = :1'; - $args = array($this->proposalid); - } + if (($this->has_arg('all') && $this->staff) || in_array($_SERVER["REMOTE_ADDR"], $img)) { + $where = '1=1'; + $args = array(); + } else { + $where = 'p.proposalid = :1'; + $args = array($this->proposalid); + } - if ($this->has_arg('cid')) { - $where .= ' AND c.containerid = :'.(sizeof($args)+1); - array_push($args, $this->arg('cid')); - } + if ($this->has_arg('cid')) { + $where .= ' AND c.containerid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('cid')); + } - if ($this->has_arg('iid')) { - $where .= ' AND i.containerinspectionid=:'.(sizeof($args)+1); - array_push($args, $this->arg('iid')); - } else { - if (!$this->has_arg('allStates')) $where .= " AND i.state='Completed'"; - } + if ($this->has_arg('iid')) { + $where .= ' AND i.containerinspectionid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('iid')); + } else { + if (!$this->has_arg('allStates')) + $where .= " AND i.state='Completed'"; + } - $tot = $this->db->pq("SELECT count(i.containerinspectionid) as tot FROM containerinspection i + $tot = $this->db->pq("SELECT count(i.containerinspectionid) as tot FROM containerinspection i INNER JOIN container c ON c.containerid = i.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $start = 0; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'sc.offset_hours, i.bltimestamp'; - - if ($this->has_arg('sort_by')) { - $cols = array( - 'SCHEDULEDTIMESTAMP' => 'i.scheduledtimestamp', - 'BLTIMESTAMP' => 'i.bltimestamp', - 'STATE' => 'i.state', - 'SCHEDULECOMPONENTID' => 'i.schedulecomponentid', - 'MANUAL' => 'i.manual', - 'IMAGESSCORED' => 'imagesscored', - 'CONTAINER' => 'c.code', - ); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $start = 0; + $end = $pp; - if ($this->has_arg('s')) { - $where .= " AND (LOWER(CONCAT(p.proposalcode, p.proposalnumber)) LIKE LOWER(CONCAT(CONCAT('%', :".(sizeof($args)+1)."), '%')) OR LOWER(c.code) LIKE LOWER(CONCAT(CONCAT('%', :".(sizeof($args)+2)."), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } - if ($this->has_arg('ty')) { - if ($this->arg('ty') == 'COMPLETED') $where .= " AND i.state = 'Completed' AND i.manual=0"; - if ($this->arg('ty') == 'INCOMPLETE') $where .= " AND i.state != 'Completed'"; - if ($this->arg('ty') == 'SCHEDULED') $where .= " AND i.scheduledtimestamp IS NOT NULL AND i.state != 'Completed'"; - if ($this->arg('ty') == 'ADHOC') $where .= " AND i.schedulecomponentid IS NULL"; - if ($this->arg('ty') == 'MANUAL') $where .= " AND i.manual=1"; - } + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'sc.offset_hours, i.bltimestamp'; + + if ($this->has_arg('sort_by')) { + $cols = array( + 'SCHEDULEDTIMESTAMP' => 'i.scheduledtimestamp', + 'BLTIMESTAMP' => 'i.bltimestamp', + 'STATE' => 'i.state', + 'SCHEDULECOMPONENTID' => 'i.schedulecomponentid', + 'MANUAL' => 'i.manual', + 'IMAGESSCORED' => 'imagesscored', + 'CONTAINER' => 'c.code', + ); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } - if ($this->has_arg('delta')) { - $join = 'LEFT OUTER JOIN containerinspection i2 ON i.containerid = i2.containerid AND i2.schedulecomponentid IS NOT NULL'; - $extc = ", ROUND(TIMESTAMPDIFF('MINUTE', min(i2.bltimestamp), i.bltimestamp)/(60*24),1) as delta, CONCAT(CONCAT(CONCAT(CONCAT(TO_CHAR(i.bltimestamp, 'HH24:MI DD-MM-YYYY'), ' (+'), ROUND(TIMESTAMPDIFF('HOUR', case when min(i2.bltimestamp) is not null then min(i2.bltimestamp) else i.bltimestamp end, i.bltimestamp)/24,1)), 'd) '), (case when i.manual=1 then '[Manual]' else (case when i.schedulecomponentid is null then '[Adhoc]' else '' end) end)) as title"; - } + if ($this->has_arg('s')) { + $where .= " AND (LOWER(CONCAT(p.proposalcode, p.proposalnumber)) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 1) . "), '%')) OR LOWER(c.code) LIKE LOWER(CONCAT(CONCAT('%', :" . (sizeof($args) + 2) . "), '%')))"; + array_push($args, $this->arg('s')); + array_push($args, $this->arg('s')); + } + + if ($this->has_arg('ty')) { + if ($this->arg('ty') == 'COMPLETED') + $where .= " AND i.state = 'Completed' AND i.manual=0"; + if ($this->arg('ty') == 'INCOMPLETE') + $where .= " AND i.state != 'Completed'"; + if ($this->arg('ty') == 'SCHEDULED') + $where .= " AND i.scheduledtimestamp IS NOT NULL AND i.state != 'Completed'"; + if ($this->arg('ty') == 'ADHOC') + $where .= " AND i.schedulecomponentid IS NULL"; + if ($this->arg('ty') == 'MANUAL') + $where .= " AND i.manual=1"; + } - $inspections = $this->db->paginate("SELECT ROUND(TIMESTAMPDIFF('HOUR', i.bltimestamp, CURRENT_TIMESTAMP)/24,1) as age, ROUND(TIMESTAMPDIFF('MINUTE', i.scheduledtimestamp, i.bltimestamp)/(24*60),2) as dwell, c.code as container, CONCAT(p.proposalcode, p.proposalnumber) as prop, TO_CHAR(min(im.modifiedtimestamp), 'DD-MM-YYYY HH24:MI') as imagesscoredtimestamp, case when count(im.blsampleimageid) > 0 then 1 else 0 end as imagesscored, - TO_CHAR(i.scheduledtimestamp, 'DD-MM-YYYY HH24:MI') as scheduledtimestamp, sc.offset_hours, i.priority, i.state, i.schedulecomponentid, i.manual, img.name as imager, it.name as inspectiontype, i.containerinspectionid, i.containerid, i.inspectiontypeid, i.temperature, TO_CHAR(i.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, i.imagerid, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), ses.visit_number) as visit, ses.visit_number, TIMESTAMPDIFF('MINUTE', i.bltimestamp, i.completedtimestamp) as duration $extc + if ($this->has_arg('delta')) { + $join = 'LEFT OUTER JOIN containerinspection i2 ON i.containerid = i2.containerid AND i2.schedulecomponentid IS NOT NULL'; + $extc = ", ROUND(TIMESTAMPDIFF('MINUTE', min(i2.bltimestamp), i.bltimestamp)/(60*24),1) as delta, CONCAT(CONCAT(CONCAT(CONCAT(TO_CHAR(i.bltimestamp, 'HH24:MI DD-MM-YYYY'), ' (+'), ROUND(TIMESTAMPDIFF('HOUR', case when min(i2.bltimestamp) is not null then min(i2.bltimestamp) else i.bltimestamp end, i.bltimestamp)/24,1)), 'd) '), (case when i.manual=1 then '[Manual]' else (case when i.schedulecomponentid is null then '[Adhoc]' else '' end) end)) as title"; + } + + $inspections = $this->db->paginate("SELECT ROUND(TIMESTAMPDIFF('HOUR', i.bltimestamp, CURRENT_TIMESTAMP)/24,1) as age, ROUND(TIMESTAMPDIFF('MINUTE', i.scheduledtimestamp, i.bltimestamp)/(24*60),2) as dwell, c.code as container, CONCAT(p.proposalcode, p.proposalnumber) as prop, TO_CHAR(min(im.modifiedtimestamp), 'DD-MM-YYYY HH24:MI') as imagesscoredtimestamp, case when count(im.blsampleimageid) > 0 then 1 else 0 end as imagesscored, + TO_CHAR(i.scheduledtimestamp, 'DD-MM-YYYY HH24:MI') as scheduledtimestamp, sc.offset_hours, i.priority, i.state, i.schedulecomponentid, i.manual, img.name as imager, it.name as inspectiontype, i.containerinspectionid, i.containerid, i.inspectiontypeid, i.temperature, TO_CHAR(i.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, i.imagerid, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, ses.visit_number, TIMESTAMPDIFF('MINUTE', i.bltimestamp, i.completedtimestamp) as duration $extc FROM containerinspection i LEFT OUTER JOIN schedulecomponent sc ON sc.schedulecomponentid = i.schedulecomponentid LEFT OUTER JOIN blsampleimage im ON im.containerinspectionid = i.containerinspectionid AND im.blsampleimagescoreid IS NOT NULL @@ -411,110 +457,128 @@ function _get_inspections() { ORDER BY $order", $args); - if ($this->has_arg('iid')) { - if (!sizeof($inspections)) $this->_error('No such inspection'); - else $this->_output($inspections[0]); - - } else $this->_output(array('total' => intval($tot[0]['TOT']), 'data' => $inspections)); + if ($this->has_arg('iid')) { + if (!sizeof($inspections)) + $this->_error('No such inspection'); + else + $this->_output($inspections[0]); + } else + $this->_output(array('total' => intval($tot[0]['TOT']), 'data' => $inspections)); + } + + + function _add_inspection() + { + if (!$this->has_arg('CONTAINERID')) + $this->_error('No container specified'); + if (!$this->has_arg('INSPECTIONTYPEID')) + $this->_error('No inspection type specified'); + + if (!$this->has_arg('IMAGERID') && !$this->has_arg('TEMPERATURE')) + $this->_error('No temperature specified'); + + if ($this->has_arg('IMAGERID')) { + $imager = $this->db->pq("SELECT temperature FROM imager WHERE imagerid=:1", array($this->arg('IMAGERID'))); + if (!sizeof($imager)) + $this->_error('No such imager'); + $temp = $imager[0]['TEMPERATURE']; + } else { + $temp = $this->arg('TEMPERATURE'); } - - function _add_inspection() { - if (!$this->has_arg('CONTAINERID')) $this->_error('No container specified'); - if (!$this->has_arg('INSPECTIONTYPEID')) $this->_error('No inspection type specified'); - - if (!$this->has_arg('IMAGERID') && !$this->has_arg('TEMPERATURE')) $this->_error('No temperature specified'); - - if ($this->has_arg('IMAGERID')) { - $imager = $this->db->pq("SELECT temperature FROM imager WHERE imagerid=:1", array($args['IMAGERID'])); - if (!sizeof($imager)) $this->error('No such imager'); - $temp = $imager[0]['TEMPERATURE']; - - } else { - $temp = $this->arg('TEMPERATURE'); - } - - $man = $this->has_arg('MANUAL') ? 1 : 0; + $man = $this->has_arg('MANUAL') ? 1 : 0; - $cont = $this->db->pq("SELECT c.containerid FROM container c + $cont = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalid = :1 AND c.containerid = :2", array($this->proposalid, $this->arg('CONTAINERID'))); - if (!sizeof($cont)) $this->error('No such container'); + if (!sizeof($cont)) { + $this->_error('No such container'); + } - $args = array('CONTAINERID' => $this->arg('CONTAINERID'), - 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), - 'IMAGERID' => $this->has_arg('IMAGERID') ? $this->arg('IMAGERID') : null, - 'TEMPERATURE' => $temp, - 'MANUAL' => $man, - 'STATE' => 'Completed', - 'BLTIMESTAMP' => $this->arg('BLTIMESTAMP'), - ); + $args = array( + 'CONTAINERID' => $this->arg('CONTAINERID'), + 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), + 'IMAGERID' => $this->has_arg('IMAGERID') ? $this->arg('IMAGERID') : null, + 'TEMPERATURE' => $temp, + 'MANUAL' => $man, + 'STATE' => 'Completed', + 'BLTIMESTAMP' => $this->arg('BLTIMESTAMP'), + ); - $id = $this->shared->_do_insert_inspection($args); - $this->_output(array('CONTAINERINSPECTIONID' => $id)); - } + $id = $this->shared->_do_insert_inspection($args); + $this->_output(array('CONTAINERINSPECTIONID' => $id)); + } - function _add_adhoc_inspection() { - if (!$this->has_arg('cid')) $this->_error('No container specified'); - if (!$this->has_arg('INSPECTIONTYPEID')) $this->_error('No inspection type specified'); + function _add_adhoc_inspection() + { + if (!$this->has_arg('cid')) + $this->_error('No container specified'); + if (!$this->has_arg('INSPECTIONTYPEID')) + $this->_error('No inspection type specified'); - $cont = $this->db->pq("SELECT c.containerid FROM container c + $cont = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalid = :1 AND c.containerid = :2", array($this->proposalid, $this->arg('cid'))); - if (!sizeof($cont)) $this->error('No such container'); + if (!sizeof($cont)) { + $this->_error('No such container'); + } - $last = $this->db->pq("SELECT i.containerid + $last = $this->db->pq( + "SELECT i.containerid FROM containerinspection i - WHERE i.state != 'Completed' AND i.schedulecomponentid IS NULL AND i.manual!=1 AND i.containerid=:1", - array($this->arg('cid'))); + WHERE i.state != 'Completed' AND i.schedulecomponentid IS NULL AND i.manual!=1 AND i.containerid=:1", + array($this->arg('cid')) + ); - if (sizeof($last)) $this->_error('Container already schedule for adhoc inspection'); + if (sizeof($last)) + $this->_error('Container already schedule for adhoc inspection'); - $args = array('CONTAINERID' => $this->arg('cid'), - 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), - 'MANUAL' => 0, - 'PRIORITY' => 99, - 'SCHEDULEDTIMESTAMP' => date('d-m-Y H:m') - ); + $args = array( + 'CONTAINERID' => $this->arg('cid'), + 'INSPECTIONTYPEID' => $this->arg('INSPECTIONTYPEID'), + 'MANUAL' => 0, + 'PRIORITY' => 99, + 'SCHEDULEDTIMESTAMP' => date('d-m-Y H:m') + ); - $id = $this->shared->_do_insert_inspection($args); - $this->_output(1); - } + $id = $this->shared->_do_insert_inspection($args); + $this->_output(1); + } - # Get a list of sample images avaialble for an inspection - function _get_inspection_images() { - $where = ''; - $args = array($this->proposalid); + # Get a list of sample images available for an inspection + function _get_inspection_images() + { + $where = ''; + $args = array($this->proposalid); - if ($this->has_arg('iid')) { - $where .= ' AND i.containerinspectionid = :'.(sizeof($args)+1); - array_push($args, $this->arg('iid')); - } - - if ($this->has_arg('imid')) { - $where .= ' AND si.blsampleimageid = :'.(sizeof($args)+1); - array_push($args, $this->arg('imid')); - } + if ($this->has_arg('iid')) { + $where .= ' AND i.containerinspectionid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('iid')); + } - if ($this->has_arg('sid')) { - $where .= ' AND si.blsampleid = :'.(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } + if ($this->has_arg('imid')) { + $where .= ' AND si.blsampleimageid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('imid')); + } + if ($this->has_arg('sid')) { + $where .= ' AND si.blsampleid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + } - $images = $this->db->pq("SELECT i.containerid, si.containerinspectionid, ROUND(TIMESTAMPDIFF('HOUR', min(i2.bltimestamp), i.bltimestamp)/24,1) as delta, si.blsampleimageid, si.blsampleid, si.micronsperpixelx, si.micronsperpixely, si.blsampleimagescoreid, si.comments, TO_CHAR(si.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, sc.name as scorename, sc.score, sc.colour as scorecolour + $images = $this->db->pq("SELECT i.containerid, si.containerinspectionid, ROUND(TIMESTAMPDIFF('HOUR', min(i2.bltimestamp), i.bltimestamp)/24,1) as delta, si.blsampleimageid, si.blsampleid, si.micronsperpixelx, si.micronsperpixely, si.blsampleimagescoreid, si.comments, TO_CHAR(si.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, sc.name as scorename, sc.score, sc.colour as scorecolour, max.maxscore, scorecolours.colour as maxscorecolour FROM blsampleimage si LEFT OUTER JOIN blsampleimagescore sc ON sc.blsampleimagescoreid = si.blsampleimagescoreid INNER JOIN containerinspection i ON i.containerinspectionid = si.containerinspectionid @@ -524,71 +588,89 @@ function _get_inspection_images() { INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid + + LEFT OUTER JOIN (SELECT blsampleid, max(score) as maxscore + FROM BLSampleImageScore sc + INNER JOIN BLSampleImage si ON sc.blsampleimagescoreid = si.blsampleimagescoreid + INNER JOIN ContainerInspection i ON i.containerinspectionid = si.containerinspectionid + GROUP BY blSampleid) as max ON max.blsampleid = si.blsampleid + LEFT OUTER JOIN (SELECT score, colour FROM BLSampleImageScore) as scorecolours ON scorecolours.score = max.maxscore + WHERE p.proposalid = :1 $where GROUP BY i.containerid, si.containerinspectionid, i.bltimestamp, si.blsampleimageid, si.blsampleid, si.micronsperpixelx, si.micronsperpixely, si.blsampleimagescoreid, si.comments, TO_CHAR(si.bltimestamp, 'DD-MM-YYYY HH24:MI'), sc.name, sc.score, sc.colour ORDER BY i.bltimestamp", $args); - if ($this->has_arg('imid')) { - if (sizeof($images)) $this->_output($images[0]); - else $this->_error('No such image'); - } else $this->_output($images); - } - - - function _add_inspection_image() { - global $upload_directory; - if (!$this->has_arg('CONTAINERINSPECTIONID')) $this->_error('No inspection specified'); - if (!$this->has_arg('BLSAMPLEID')) $this->_error('No sample specified'); - - if (array_key_exists('IMAGE', $_FILES)) { - if ($_FILES['IMAGE']['name']) { - $info = pathinfo($_FILES['IMAGE']['name']); - - if (in_array($info['extension'], array('jpg', 'png'))) { - $id = $this->shared->_do_insert_inspection_image(array( - 'CONTAINERINSPECTIONID' => $this->arg('CONTAINERINSPECTIONID'), - 'BLSAMPLEID' => $this->arg('BLSAMPLEID'), - 'PROPOSALID' => $this->proposalid, - )); - - # Follow same syntax as formulatrix images - $root = $upload_directory.'/imaging/'.$this->arg('CONTAINERINSPECTIONID'); - if (!file_exists($root)) mkdir($root, 0777, true); - $file = $root.'/'.$id.'.'.$info['extension']; - move_uploaded_file($_FILES['IMAGE']['tmp_name'], $file); - $this->_create_thumb($file); - - $this->db->pq("UPDATE blsampleimage SET imagefullpath=:1 WHERE blsampleimageid=:2", array($file, $id)); - - $this->_output(array('BLSAMPLEIMAGEID' => $id)); - } + if ($this->has_arg('imid')) { + if (sizeof($images)) + $this->_output($images[0]); + else + $this->_error('No such image'); + } else + $this->_output($images); + } + + + function _add_inspection_image() + { + global $upload_directory; + if (!$this->has_arg('CONTAINERINSPECTIONID')) + $this->_error('No inspection specified'); + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); + + if (array_key_exists('IMAGE', $_FILES)) { + if ($_FILES['IMAGE']['name']) { + $info = pathinfo($_FILES['IMAGE']['name']); + + if (in_array($info['extension'], array('jpg', 'png'))) { + $id = $this->shared->_do_insert_inspection_image(array( + 'CONTAINERINSPECTIONID' => $this->arg('CONTAINERINSPECTIONID'), + 'BLSAMPLEID' => $this->arg('BLSAMPLEID'), + 'PROPOSALID' => $this->proposalid, + )); + + # Follow same syntax as formulatrix images + $root = $upload_directory . '/imaging/' . $this->arg('CONTAINERINSPECTIONID'); + if (!file_exists($root)) + mkdir($root, 0777, true); + $file = $root . '/' . $id . '.' . $info['extension']; + move_uploaded_file($_FILES['IMAGE']['tmp_name'], $file); + $this->_create_thumb($file); + + $this->db->pq("UPDATE blsampleimage SET imagefullpath=:1 WHERE blsampleimageid=:2", array($file, $id)); + + $this->_output(array('BLSAMPLEIMAGEID' => $id)); } } } + } - function _create_thumb($file) { - $info = pathinfo($file); - $img = imagecreatefromjpeg($file); - $w = imagesx($img); - $h = imagesy($img); + function _create_thumb($file) + { + $info = pathinfo($file); + $img = imagecreatefromjpeg($file); + $w = imagesx($img); + $h = imagesy($img); - $nw = 200; - $nh = floor((200/$w)*$h); + $nw = 200; + $nh = floor((200 / $w) * $h); - $new = imagecreatetruecolor($nw, $nh); - imagecopyresized($new, $img, 0, 0, 0, 0, $nw, $nh, $w, $h); + $new = imagecreatetruecolor($nw, $nh); + imagecopyresized($new, $img, 0, 0, 0, 0, $nw, $nh, $w, $h); - $info = pathinfo($file); - imagejpeg($new, str_replace('.'.$info['extension'], 'th.'.$info['extension'], $file)); - } + $info = pathinfo($file); + imagejpeg($new, str_replace('.' . $info['extension'], 'th.' . $info['extension'], $file)); + } - function _update_inspection_image() { - if (!$this->has_arg('imid')) $this->_error('No inspection image specified'); + function _update_inspection_image() + { + if (!$this->has_arg('imid')) + $this->_error('No inspection image specified'); - $image = $this->db->pq("SELECT si.blsampleimageid + $image = $this->db->pq("SELECT si.blsampleimageid FROM blsampleimage si INNER JOIN containerinspection i ON i.containerinspectionid = si.containerinspectionid INNER JOIN container c ON c.containerid = i.containerid @@ -597,51 +679,55 @@ function _update_inspection_image() { INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalid = :1 AND si.blsampleimageid=:2", array($this->proposalid, $this->arg('imid'))); - if (!sizeof($image)) $this->_error('No such image'); + if (!sizeof($image)) + $this->_error('No such image'); - foreach(array('COMMENTS', 'BLSAMPLEIMAGESCOREID') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE blsampleimage SET '.$f.'=:1, modifiedtimestamp=CURRENT_TIMESTAMP WHERE blsampleimageid=:2', array($this->arg($f), $this->arg('imid'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('COMMENTS', 'BLSAMPLEIMAGESCOREID') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE blsampleimage SET ' . $f . '=:1, modifiedtimestamp=CURRENT_TIMESTAMP WHERE blsampleimageid=:2', array($this->arg($f), $this->arg('imid'))); + $this->_output(array($f => $this->arg($f))); } - } + } - function _get_image_scores() { - // $db = array( - // array(1, 'Clear', 0, '#ccccc'), - // array(2, 'Contaminated', 1, '#fffd96'), - // array(3, 'Light Precipitate', 2, '#fdfd96'), - // array(4, 'Phase Separation', 5, '#fdfd96'), - // array(5, 'Spherulites / Interesting', 6, '#ffb347'), - // array(6, 'Microcrystals / Sea Urchin', 6, '#ffb347'), - // array(7, '1D Crystals', 7, '#87ceeb'), - // array(8, '2D Crystals', 8, '#77dd77'), - // array(9, '3D Crystals', 9, '#77dd77'), - // array(10, 'Heavy Precipitate', 3, '#ff6961'), - // array(11, 'Gelatinous precipitate', 4, '') - // ); + function _get_image_scores() + { + // $db = array( + // array(1, 'Clear', 0, '#ccccc'), + // array(2, 'Contaminated', 1, '#fffd96'), + // array(3, 'Light Precipitate', 2, '#fdfd96'), + // array(4, 'Phase Separation', 5, '#fdfd96'), + // array(5, 'Spherulites / Interesting', 6, '#ffb347'), + // array(6, 'Microcrystals / Sea Urchin', 6, '#ffb347'), + // array(7, '1D Crystals', 7, '#87ceeb'), + // array(8, '2D Crystals', 8, '#77dd77'), + // array(9, '3D Crystals', 9, '#77dd77'), + // array(10, 'Heavy Precipitate', 3, '#ff6961'), + // array(11, 'Gelatinous precipitate', 4, '') + // ); - // foreach ($db as $b) { - // $this->db->pq("INSERT INTO blsampleimagescore (blsampleimagescoreid, name, score, colour) - // VALUES (s_blsampleimagescore.nextval, :1, :2, :3)", array($b[1], $b[2], $b[3])); - // } + // foreach ($db as $b) { + // $this->db->pq("INSERT INTO blsampleimagescore (blsampleimagescoreid, name, score, colour) + // VALUES (s_blsampleimagescore.nextval, :1, :2, :3)", array($b[1], $b[2], $b[3])); + // } - $scores = $this->db->pq("SELECT blsampleimagescoreid, name, score, colour, CONCAT(score, ' - ', name) as title FROM blsampleimagescore ORDER BY score"); - $this->_output($scores); - } + $scores = $this->db->pq("SELECT blsampleimagescoreid, name, score, colour, CONCAT(score, ' - ', name) as title FROM blsampleimagescore ORDER BY score"); + $this->_output($scores); + } - function _get_auto_score_schemas() { - if (!$this->has_arg('CONTAINERINSPECTIONID')) $this->_error('No container inspection specified'); + function _get_auto_score_schemas() + { + if (!$this->has_arg('CONTAINERINSPECTIONID')) + $this->_error('No container inspection specified'); - $rows = $this->db->pq("SELECT distinct sass.blsampleimageautoscoreschemaid, sass.schemaname + $rows = $this->db->pq( + "SELECT distinct sass.blsampleimageautoscoreschemaid, sass.schemaname FROM containerinspection ci INNER JOIN blsampleimage si ON si.containerinspectionid = ci.containerinspectionid INNER JOIN blsampleimage_has_autoscoreclass as shasc ON si.blsampleimageid = shasc.blsampleimageid @@ -650,17 +736,22 @@ function _get_auto_score_schemas() { INNER JOIN container c ON c.containerid = ci.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid - WHERE ci.containerinspectionid = :1 AND s.proposalid = :2", - array($this->arg("CONTAINERINSPECTIONID"), $this->proposalid)); - - $this->_output($rows); - } - - function _get_auto_scores() { - if (!$this->has_arg('CONTAINERINSPECTIONID')) $this->_error('No container inspection specified'); - if (!$this->has_arg('BLSAMPLEIMAGEAUTOSCORESCHEMAID')) $this->_error('No schema specified'); - - $rows = $this->db->pq("SELECT si.blsampleimageid, sasc.blsampleimageautoscoreclassid, sasc.scoreclass, shasc.probability + WHERE ci.containerinspectionid = :1 AND s.proposalid = :2", + array($this->arg("CONTAINERINSPECTIONID"), $this->proposalid) + ); + + $this->_output($rows); + } + + function _get_auto_scores() + { + if (!$this->has_arg('CONTAINERINSPECTIONID')) + $this->_error('No container inspection specified'); + if (!$this->has_arg('BLSAMPLEIMAGEAUTOSCORESCHEMAID')) + $this->_error('No schema specified'); + + $rows = $this->db->pq( + "SELECT si.blsampleimageid, sasc.blsampleimageautoscoreclassid, sasc.scoreclass, shasc.probability FROM containerinspection ci INNER JOIN blsampleimage si ON si.containerinspectionid = ci.containerinspectionid INNER JOIN blsampleimage_has_autoscoreclass as shasc ON si.blsampleimageid = shasc.blsampleimageid @@ -669,28 +760,32 @@ function _get_auto_scores() { INNER JOIN container c ON c.containerid = ci.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid - WHERE ci.containerinspectionid = :1 AND s.proposalid = :2 AND sass.blsampleimageautoscoreschemaid = :3", - array($this->arg("CONTAINERINSPECTIONID"), $this->proposalid, $this->arg('BLSAMPLEIMAGEAUTOSCORESCHEMAID'))); - - $inspection = array(); - foreach ($rows as $r) { - if (!array_key_exists($r['BLSAMPLEIMAGEID'], $inspection)) $inspection[$r['BLSAMPLEIMAGEID']] = array( + WHERE ci.containerinspectionid = :1 AND s.proposalid = :2 AND sass.blsampleimageautoscoreschemaid = :3", + array($this->arg("CONTAINERINSPECTIONID"), $this->proposalid, $this->arg('BLSAMPLEIMAGEAUTOSCORESCHEMAID')) + ); + + $inspection = array(); + foreach ($rows as $r) { + if (!array_key_exists($r['BLSAMPLEIMAGEID'], $inspection)) + $inspection[$r['BLSAMPLEIMAGEID']] = array( 'BLSAMPLEIMAGEID' => $r['BLSAMPLEIMAGEID'], 'CLASSES' => array(), ); - $inspection[$r['BLSAMPLEIMAGEID']]['CLASSES'][$r['SCORECLASS']] = floatval($r['PROBABILITY']); - } - - $this->_output(array_values($inspection)); + $inspection[$r['BLSAMPLEIMAGEID']]['CLASSES'][$r['SCORECLASS']] = floatval($r['PROBABILITY']); } + $this->_output(array_values($inspection)); + } - function _get_image() { - if (!$this->has_arg('imid')) $this->_error('No image specified'); - $im = $this->db->pq("SELECT si.imagefullpath + function _get_image() + { + if (!$this->has_arg('imid')) + $this->_error('No image specified'); + + $im = $this->db->pq("SELECT si.imagefullpath FROM blsampleimage si INNER JOIN containerinspection i ON i.containerinspectionid = si.containerinspectionid INNER JOIN container c ON c.containerid = i.containerid @@ -699,42 +794,46 @@ function _get_image() { INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE si.blsampleimageid=:1", array($this->arg('imid'))); - if (!sizeof($im)) $this->_error('No such image'); - else { - $file = $im[0]['IMAGEFULLPATH']; - if (file_exists($file)) { - $ext = pathinfo($file, PATHINFO_EXTENSION); - if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) $head = 'image/'.$ext; - - if (!$this->has_arg('f')) { - $th = str_replace('.'.$ext, 'th.'.$ext, $file); - if (!file_exists($th)) $this->_create_thumb($file); - $file = $th; - } - - $expires = 60*60*24*14; - // $this->app->response->headers->set('Pragma', 'public'); - $this->app->response->headers->set('Cache-Control', 'max-age='.$expires); - $this->app->response->headers->set('Expires', gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT'); - $this->app->contentType($head); - $this->app->response()->header('Content-Length', filesize($file)); - readfile($file); - - } else $this->_error('No such image'); - } + if (!sizeof($im)) + $this->_error('No such image'); + else { + $file = $im[0]['IMAGEFULLPATH']; + if (file_exists($file)) { + $ext = pathinfo($file, PATHINFO_EXTENSION); + if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) + $head = 'image/' . $ext; + + if (!$this->has_arg('f')) { + $th = str_replace('.' . $ext, 'th.' . $ext, $file); + if (!file_exists($th)) + $this->_create_thumb($file); + $file = $th; + } + + $expires = 60 * 60 * 24 * 14; + // $this->app->response->headers->set('Pragma', 'public'); + $this->app->response->headers->set('Cache-Control', 'max-age=' . $expires); + $this->app->response->headers->set('Expires', gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT'); + $this->app->contentType($head); + $this->app->response()->header('Content-Length', filesize($file)); + readfile($file); + } else + $this->_error('No such image'); } + } - function _get_screens() { - $where = ' (s.proposalid=:1 OR s.global = 1)'; - $args = array($this->proposalid); + function _get_screens() + { + $where = ' (s.proposalid=:1 OR s.global = 1)'; + $args = array($this->proposalid); - if ($this->has_arg('scid')) { - $where .= ' AND s.screenid=:'.(sizeof($args)+1); - array_push($args, $this->arg('scid')); - } + if ($this->has_arg('scid')) { + $where .= ' AND s.screenid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('scid')); + } - $screens = $this->db->pq("SELECT 96 as capacity, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.global, s.name, s.screenid, s.proposalid, count(distinct sg.screencomponentgroupid) as groups, count(distinct sc.screencomponentid) as components + $screens = $this->db->pq("SELECT 96 as capacity, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.global, s.name, s.screenid, s.proposalid, count(distinct sg.screencomponentgroupid) as groups, count(distinct sc.screencomponentid) as components FROM screen s LEFT OUTER JOIN screencomponentgroup sg ON sg.screenid = s.screenid LEFT OUTER JOIN screencomponent sc ON sc.screencomponentgroupid = sg.screencomponentgroupid @@ -742,58 +841,66 @@ function _get_screens() { WHERE $where GROUP BY CONCAT(p.proposalcode, p.proposalnumber), s.global, s.name, s.screenid, s.proposalid", $args); - if ($this->has_arg('scid')) { - if (sizeof($screens)) $this->_output($screens[0]); - else $this->_error('No such screen'); - - } else $this->_output($screens); - } + if ($this->has_arg('scid')) { + if (sizeof($screens)) + $this->_output($screens[0]); + else + $this->_error('No such screen'); + } else + $this->_output($screens); + } - function _add_screen() { - if (!$this->has_arg('NAME')) $this->_error('No screen name provided'); + function _add_screen() + { + if (!$this->has_arg('NAME')) + $this->_error('No screen name provided'); - $this->db->pq("INSERT INTO screen (screenid, name, proposalid) + $this->db->pq("INSERT INTO screen (screenid, name, proposalid) VALUES (s_screen.nextval, :1, :2) RETURNING screenid INTO :id", array($this->arg('NAME'), $this->proposalid)); - $this->_output(array('SCREENID' => $this->db->id())); - } + $this->_output(array('SCREENID' => $this->db->id())); + } - function _update_screen() { - if (!$this->has_arg('scid')) $this->_error('No screen specified'); + function _update_screen() + { + if (!$this->has_arg('scid')) + $this->_error('No screen specified'); - $sc = $this->db->pq("SELECT sc.screenid + $sc = $this->db->pq("SELECT sc.screenid FROM screen sc - WHERE sc.screenid = :1 and sc.proposalid= :2", array($this->arg('scid'), $this->proposalid)); + WHERE sc.screenid = :1 and (sc.global=1 or sc.proposalid= :2)", array($this->arg('scid'), $this->proposalid)); - if (!sizeof($sc)) $this->_error('No such screen'); + if (!sizeof($sc)) + $this->_error('No such screen'); - foreach(array('NAME', 'GLOBAL') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE screen SET '.$f.'=:1 WHERE screenid=:2', array($this->arg($f), $this->arg('scid'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('NAME', 'GLOBAL') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE screen SET ' . $f . '=:1 WHERE screenid=:2', array($this->arg($f), $this->arg('scid'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _get_screen_componentgroups() { - $where = ' (s.proposalid=:1 OR s.global = 1)'; - $args = array($this->proposalid); + function _get_screen_componentgroups() + { + $where = ' (s.proposalid=:1 OR s.global = 1)'; + $args = array($this->proposalid); - if ($this->has_arg('scgid')) { - $where .= ' AND sg.screencomponentgroupid=:'.(sizeof($args)+1); - array_push($args, $this->arg('scgid')); - } + if ($this->has_arg('scgid')) { + $where .= ' AND sg.screencomponentgroupid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('scgid')); + } - if ($this->has_arg('scid')) { - $where .= ' AND sg.screenid=:'.(sizeof($args)+1); - array_push($args, $this->arg('scid')); - } + if ($this->has_arg('scid')) { + $where .= ' AND sg.screenid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('scid')); + } - $sgs = $this->db->pq("SELECT sg.screenid, sg.screencomponentgroupid, sg.position, count(sc.screencomponentid) as components + $sgs = $this->db->pq("SELECT sg.screenid, sg.screencomponentgroupid, sg.position, count(sc.screencomponentid) as components FROM screencomponentgroup sg INNER JOIN screen s ON s.screenid = sg.screenid LEFT OUTER JOIN screencomponent sc ON sc.screencomponentgroupid = sg.screencomponentgroupid @@ -801,98 +908,118 @@ function _get_screen_componentgroups() { GROUP BY sg.screenid, sg.screencomponentgroupid, sg.position ORDER BY sg.position", $args); - if ($this->has_arg('scgid')) { - if (sizeof($sgs)) $this->_output($sgs[0]); - else $this->_error('No such screen component group'); - - } else $this->_output($sgs); - } - - function _add_screen_componentgroup() { - $id = $this->_do_add_screen_componentgroup(); - $this->_output(array('SCREENCOMPONENTGROUPID' => $id)); - } - - function _add_screen_componentgroups() { - if (!$this->has_arg('collection')) $this->_error('No groups specified'); - - foreach ($this->arg('collection') as $g) { - if (array_key_exists('SCREENID', $g)) $this->args['SCREENID'] = $g['SCREENID']; - else unset($this->args['SCREENID']); - - if (array_key_exists('SCREENCOMPONENTGROUPID', $g)) continue; + if ($this->has_arg('scgid')) { + if (sizeof($sgs)) + $this->_output($sgs[0]); + else + $this->_error('No such screen component group'); + } else + $this->_output($sgs); + } + + function _add_screen_componentgroup() + { + $id = $this->_do_add_screen_componentgroup(); + $this->_output(array('SCREENCOMPONENTGROUPID' => $id)); + } + + function _add_screen_componentgroups() + { + if (!$this->has_arg('collection')) + $this->_error('No groups specified'); + + foreach ($this->arg('collection') as $g) { + if (array_key_exists('SCREENID', $g)) + $this->args['SCREENID'] = $g['SCREENID']; + else + unset($this->args['SCREENID']); - if (array_key_exists('POSITION', $g)) $this->args['POSITION'] = $g['POSITION']; - else unset($this->args['POSITION']); + if (array_key_exists('SCREENCOMPONENTGROUPID', $g)) + continue; - $this->_do_add_screen_componentgroup(); - } + if (array_key_exists('POSITION', $g)) + $this->args['POSITION'] = $g['POSITION']; + else + unset($this->args['POSITION']); - $this->args['scid'] = $this->arg('SCREENID'); - $this->_get_screen_componentgroups(); + $this->_do_add_screen_componentgroup(); } - function _do_add_screen_componentgroup() { - if (!$this->has_arg('SCREENID')) $this->_error('No screen specified'); - if (!$this->has_arg('POSITION')) $this->_error('No position specified'); + $this->args['scid'] = $this->arg('SCREENID'); + $this->_get_screen_componentgroups(); + } - $sc = $this->db->pq("SELECT sc.screenid + function _do_add_screen_componentgroup() + { + if (!$this->has_arg('SCREENID')) + $this->_error('No screen specified'); + if (!$this->has_arg('POSITION')) + $this->_error('No position specified'); + + $sc = $this->db->pq("SELECT sc.screenid FROM screen sc WHERE sc.screenid = :1 and sc.proposalid= :2", array($this->arg('SCREENID'), $this->proposalid)); - if (!sizeof($sc)) $this->_error('No such screen'); + if (!sizeof($sc)) + $this->_error('No such screen'); - $this->db->pq("INSERT INTO screencomponentgroup (screencomponentgroupid, screenid, position) + $this->db->pq("INSERT INTO screencomponentgroup (screencomponentgroupid, screenid, position) VALUES (s_screencomponentgroup.nextval, :1, :2) RETURNING screencomponentgroupid INTO :id", array($this->arg('SCREENID'), $this->arg('POSITION'))); - return $this->db->id(); - } + return $this->db->id(); + } - function _update_screen_componentgroup() { - if (!$this->has_arg('scgid')) $this->_error('No screen component group specified'); + function _update_screen_componentgroup() + { + if (!$this->has_arg('scgid')) + $this->_error('No screen component group specified'); - $sc = $this->db->pq("SELECT s.screenid + $sc = $this->db->pq( + "SELECT s.screenid FROM screen s INNER JOIN screencomponentgroup sg ON sg.screenid = s.screenid - WHERE s.proposalid = :1 and sg.screencomponentgroupid = :2", - array($this->proposalid, $this->arg('scgid'))); + WHERE s.proposalid = :1 and sg.screencomponentgroupid = :2", + array($this->proposalid, $this->arg('scgid')) + ); - if (!sizeof($sc)) $this->_error('No such screen component group'); + if (!sizeof($sc)) + $this->_error('No such screen component group'); - foreach(array('SCREENID', 'POSITION') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE screencomponentgroup SET '.$f.'=:1 WHERE screencomponentgroupid=:2', array($this->arg($f), $this->arg('scgid'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('SCREENID', 'POSITION') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE screencomponentgroup SET ' . $f . '=:1 WHERE screencomponentgroupid=:2', array($this->arg($f), $this->arg('scgid'))); + $this->_output(array($f => $this->arg($f))); } } + } - // This should probably be an addition to _get_protein in class.sample.php - function _get_screen_components() { - $where = ' (s.proposalid=:1 OR s.global = 1)'; - $args = array($this->proposalid); + // This should probably be an addition to _get_protein in class.sample.php + function _get_screen_components() + { + $where = ' (s.proposalid=:1 OR s.global = 1)'; + $args = array($this->proposalid); - if ($this->has_arg('sccid')) { - $where .= ' AND sc.screencomponentid=:'.(sizeof($args)+1); - array_push($args, $this->arg('sccid')); - } + if ($this->has_arg('sccid')) { + $where .= ' AND sc.screencomponentid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sccid')); + } - if ($this->has_arg('scgid')) { - $where .= ' AND sc.screencomponentgroupid=:'.(sizeof($args)+1); - array_push($args, $this->arg('scgid')); - } + if ($this->has_arg('scgid')) { + $where .= ' AND sc.screencomponentgroupid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('scgid')); + } - if ($this->has_arg('scid')) { - $where .= ' AND sg.screenid=:'.(sizeof($args)+1); - array_push($args, $this->arg('scid')); - } + if ($this->has_arg('scid')) { + $where .= ' AND sg.screenid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('scid')); + } - $sgs = $this->db->pq("SELECT sg.position, s.screenid, sc.screencomponentid, sc.screencomponentgroupid, sc.concentration, sc.ph, sc.componentid, p.acronym, case when p.name is not null then p.name else p.acronym end as component, ct.symbol as unit, 1 as hasph + $sgs = $this->db->pq("SELECT sg.position, s.screenid, sc.screencomponentid, sc.screencomponentgroupid, sc.concentration, sc.ph, sc.componentid, p.acronym, case when p.name is not null then p.name else p.acronym end as component, ct.symbol as unit, 1 as hasph FROM screencomponent sc INNER JOIN protein p ON sc.componentid = p.proteinid LEFT OUTER JOIN concentrationtype ct ON ct.concentrationtypeid = p.concentrationtypeid @@ -900,126 +1027,156 @@ function _get_screen_components() { INNER JOIN screen s ON s.screenid = sg.screenid WHERE $where", $args); - if ($this->has_arg('sccid')) { - if (sizeof($sgs)) $this->_output($sgs[0]); - else $this->_error('No such screen component'); - - } else $this->_output($sgs); - } + if ($this->has_arg('sccid')) { + if (sizeof($sgs)) + $this->_output($sgs[0]); + else + $this->_error('No such screen component'); + } else + $this->_output($sgs); + } - function _add_screen_component() { - $id = $this->_do_add_screen_component(); - $this->_output(array('SCREENCOMPONENTID' => $this->db->id())); - } + function _add_screen_component() + { + $id = $this->_do_add_screen_component(); + $this->_output(array('SCREENCOMPONENTID' => $this->db->id())); + } - function _add_screen_components() { - if (!$this->has_arg('collection')) $this->_error('No collection specified'); + function _add_screen_components() + { + if (!$this->has_arg('collection')) + $this->_error('No collection specified'); - foreach ($this->arg('collection') as $c) { - if (array_key_exists('SCREENCOMPONENTID', $c)) continue; + foreach ($this->arg('collection') as $c) { + if (array_key_exists('SCREENCOMPONENTID', $c)) + continue; - $this->args['SCREENCOMPONENTGROUPID'] = $c['SCREENCOMPONENTGROUPID']; - $this->args['COMPONENTID'] = $c['COMPONENTID']; + $this->args['SCREENCOMPONENTGROUPID'] = $c['SCREENCOMPONENTGROUPID']; + $this->args['COMPONENTID'] = $c['COMPONENTID']; - if (array_key_exists('CONCENTRATION', $c)) $this->args['CONCENTRATION'] = $c['CONCENTRATION']; - else unset($this->args['CONCENTRATION']); + if (array_key_exists('CONCENTRATION', $c)) + $this->args['CONCENTRATION'] = $c['CONCENTRATION']; + else + unset($this->args['CONCENTRATION']); - if (array_key_exists('PH', $c)) $this->args['PH'] = $c['PH']; - else unset($this->args['PH']); + if (array_key_exists('PH', $c)) + $this->args['PH'] = $c['PH']; + else + unset($this->args['PH']); - $this->_do_add_screen_component(); - } + $this->_do_add_screen_component(); + } - $scids = $this->db->pq("SELECT screenid FROM screencomponentgroup WHERE screencomponentgroupid=:1", array($c['SCREENCOMPONENTGROUPID'])); + $scids = $this->db->pq("SELECT screenid FROM screencomponentgroup WHERE screencomponentgroupid=:1", array($c['SCREENCOMPONENTGROUPID'])); - if (sizeof($scids)) { - $this->args['scid'] = $scids[0]['SCREENID']; - $this->_get_screen_components(); - } + if (sizeof($scids)) { + $this->args['scid'] = $scids[0]['SCREENID']; + $this->_get_screen_components(); } + } - function _do_add_screen_component() { - if (!$this->has_arg('SCREENCOMPONENTGROUPID')) $this->_error('No screen component group specified'); - if (!$this->has_arg('COMPONENTID')) $this->_error('No component specified'); + function _do_add_screen_component() + { + if (!$this->has_arg('SCREENCOMPONENTGROUPID')) + $this->_error('No screen component group specified'); + if (!$this->has_arg('COMPONENTID')) + $this->_error('No component specified'); - $sc = $this->db->pq("SELECT s.screenid + $sc = $this->db->pq( + "SELECT s.screenid FROM screen s INNER JOIN screencomponentgroup sg ON sg.screenid = s.screenid - WHERE s.proposalid = :1 and sg.screencomponentgroupid = :2", - array($this->proposalid, $this->arg('SCREENCOMPONENTGROUPID'))); + WHERE s.proposalid = :1 and sg.screencomponentgroupid = :2", + array($this->proposalid, $this->arg('SCREENCOMPONENTGROUPID')) + ); - if (!sizeof($sc)) $this->_error('No such screen component group'); + if (!sizeof($sc)) + $this->_error('No such screen component group'); - $ph = $this->has_arg('PH') ? $this->arg('PH') : null; - $conc = $this->has_arg('CONCENTRATION') ? $this->arg('CONCENTRATION') : null; - - $this->db->pq("INSERT INTO screencomponent (screencomponentid, screencomponentgroupid, componentid, concentration, ph) + $ph = $this->has_arg('PH') ? $this->arg('PH') : null; + $conc = $this->has_arg('CONCENTRATION') ? $this->arg('CONCENTRATION') : null; + + $this->db->pq("INSERT INTO screencomponent (screencomponentid, screencomponentgroupid, componentid, concentration, ph) VALUES (s_screencomponent.nextval, :1, :2, :3, :4) RETURNING screencomponentid INTO :id", array($this->arg('SCREENCOMPONENTGROUPID'), $this->arg('COMPONENTID'), $conc, $ph)); - return $this->db->id(); - } + return $this->db->id(); + } - function _update_screen_component() { - if (!$this->has_arg('sccid')) $this->_error('No screen component specified'); + function _update_screen_component() + { + if (!$this->has_arg('sccid')) + $this->_error('No screen component specified'); - $sc = $this->db->pq("SELECT s.screenid + $sc = $this->db->pq( + "SELECT s.screenid FROM screen s INNER JOIN screencomponentgroup sg ON sg.screenid = s.screenid INNER JOIN screencomponent sc ON sc.screencomponentgroupid = sg.screencomponentgroupid - WHERE s.proposalid = :1 and sc.screencomponentid = :2", - array($this->proposalid, $this->arg('sccid'))); + WHERE s.proposalid = :1 and sc.screencomponentid = :2", + array($this->proposalid, $this->arg('sccid')) + ); - if (!sizeof($sc)) $this->_error('No such screen component'); + if (!sizeof($sc)) + $this->_error('No such screen component'); - foreach(array('SCREENCOMPONENTGROUPID', 'COMPONENTID', 'CONCENTRATION', 'PH') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE screencomponent SET '.$f.'=:1 WHERE screencomponentid=:2', array($this->arg($f), $this->arg('sccid'))); - $this->_output(array($f => $this->arg($f))); - } + foreach (array('SCREENCOMPONENTGROUPID', 'COMPONENTID', 'CONCENTRATION', 'PH') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE screencomponent SET ' . $f . '=:1 WHERE screencomponentid=:2', array($this->arg($f), $this->arg('sccid'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _update_screen_component_full() { - if (!$this->has_arg('sccid')) $this->_error('No screen component specified'); + function _update_screen_component_full() + { + if (!$this->has_arg('sccid')) + $this->_error('No screen component specified'); - $sc = $this->db->pq("SELECT s.screenid + $sc = $this->db->pq( + "SELECT s.screenid FROM screen s INNER JOIN screencomponentgroup sg ON sg.screenid = s.screenid INNER JOIN screencomponent sc ON sc.screencomponentgroupid = sg.screencomponentgroupid - WHERE s.proposalid = :1 and sc.screencomponentid = :2", - array($this->proposalid, $this->arg('sccid'))); + WHERE s.proposalid = :1 and sc.screencomponentid = :2", + array($this->proposalid, $this->arg('sccid')) + ); - if (!sizeof($sc)) $this->_error('No such screen component'); + if (!sizeof($sc)) + $this->_error('No such screen component'); - $ph = $this->has_arg('PH') ? $this->arg('PH') : null; - $conc = $this->has_arg('CONCENTRATION') ? $this->arg('CONCENTRATION') : null; + $ph = $this->has_arg('PH') ? $this->arg('PH') : null; + $conc = $this->has_arg('CONCENTRATION') ? $this->arg('CONCENTRATION') : null; - $this->db->pq('UPDATE screencomponent SET concentration=:1,ph=:2 WHERE screencomponentid=:3', array($conc, $ph, $this->arg('sccid'))); - - $this->_output(array( - 'CONCENTRATION' => $conc, - 'PH' => $ph, - )); - - } + $this->db->pq('UPDATE screencomponent SET concentration=:1,ph=:2 WHERE screencomponentid=:3', array($conc, $ph, $this->arg('sccid'))); + $this->_output(array( + 'CONCENTRATION' => $conc, + 'PH' => $ph, + )); + } - function _delete_screen_component() { - if (!$this->has_arg('sccid')) $this->_error('No screen component specified'); - $sc = $this->db->pq("SELECT s.screenid + function _delete_screen_component() + { + if (!$this->has_arg('sccid')) + $this->_error('No screen component specified'); + + $sc = $this->db->pq( + "SELECT s.screenid FROM screen s INNER JOIN screencomponentgroup sg ON sg.screenid = s.screenid INNER JOIN screencomponent sc ON sc.screencomponentgroupid = sg.screencomponentgroupid - WHERE s.proposalid = :1 and sc.screencomponentid = :2", - array($this->proposalid, $this->arg('sccid'))); + WHERE s.proposalid = :1 and sc.screencomponentid = :2", + array($this->proposalid, $this->arg('sccid')) + ); - if (!sizeof($sc)) $this->_error('No such screen component'); + if (!sizeof($sc)) + $this->_error('No such screen component'); - $this->db->pq("DELETE FROM screencomponent WHERE screencomponentid=:1", array($this->arg('sccid'))); - $this->_output(1); - } + $this->db->pq("DELETE FROM screencomponent WHERE screencomponentid=:1", array($this->arg('sccid'))); + $this->_output(1); + } } diff --git a/api/src/Page/PDF.php b/api/src/Page/PDF.php index 57cd26d8f..48d6944d3 100644 --- a/api/src/Page/PDF.php +++ b/api/src/Page/PDF.php @@ -2,14 +2,9 @@ namespace SynchWeb\Page; -// For mpdf < 7.0 we should define our own temp dirs instead of using vendor/mpdf -// sys_get_temp_dir returns the directory used by php-fpm (handles PrivateTmp) -define("_MPDF_TEMP_PATH", sys_get_temp_dir() . "/mpdf/temp/"); -define("_MPDF_TTFONTDATAPATH", sys_get_temp_dir() . "/mpdf/ttfontdata/"); - -use mPDF; use Slim\Slim; use SynchWeb\Page; +use Mpdf\Mpdf; # ------------------------------------------------------------------------ # PDF Generation @@ -20,59 +15,65 @@ class PDF extends Page { - private $vars = array(); - - public static $arg_list = array('visit' => '\w+\d+\-\d+', - 'sid' => '\d+', - 'p' => '\d', - 'cid' => '\d+', - 'did' => '\d+', - 'sid' => '\d+', - 'labels' => '[\w-]+', - 'month' => '\d\d-\d\d\d\d', - ); - - public static $dispatch = array(array('/sid/:sid/prop/:prop', 'get', '_shipment_label'), - array('/container(/sid/:sid)(/cid/:cid)(/did/:did)/prop/:prop', 'get', '_container_report'), - array('/report/visit/:visit', 'get', '_visit_report'), - array('/sheets', 'get', '_generate_sheets'), - array('/awb/sid/:sid', 'get', '_get_awb'), - array('/manifest', 'get', '_get_manifest'), - ); - - function __construct(Slim $app, $db, $user) { - // Call parent constructor to register our routes - parent::__construct($app, $db, $user); - - // Fix for mpdf < 7.0 to ensure temp dir exists - // Creating the temp folder here means we have the correct permissions - // Using separate folders for temp and font data so mpdf does not try to delete ttfontdata - $mpdf_temp = sys_get_temp_dir() . "/mpdf/temp/"; - $mpdf_fontdata = sys_get_temp_dir() . "/mpdf/ttfontdata/"; - - if (!is_dir($mpdf_temp)) { - mkdir($mpdf_temp, 0775, true); - } - if (!is_dir($mpdf_fontdata)) { - mkdir($mpdf_fontdata, 0775, true); - } + private $vars = array(); + + public static $arg_list = array( + 'visit' => '\w+\d+\-\d+', + 'p' => '\d', + 'cid' => '\d+', + 'did' => '\d+', + 'sid' => '\d+', + 'labels' => '[\w\-]+', + 'month' => '\d\d-\d\d\d\d', + ); + + public static $dispatch = array( + array('/sid/:sid/prop/:prop', 'get', '_shipment_label'), + array('/container(/sid/:sid)(/cid/:cid)(/did/:did)/prop/:prop', 'get', '_container_report'), + array('/report/visit/:visit', 'get', '_visit_report'), + array('/sheets', 'get', '_generate_sheets'), + array('/awb/sid/:sid', 'get', '_get_awb'), + array('/manifest', 'get', '_get_manifest'), + ); + + function __construct(Slim $app, $db, $user) + { + // Call parent constructor to register our routes + parent::__construct($app, $db, $user); + + // Creating the temp folder here means we have the correct permissions + // Using separate folders for temp and font data so mpdf does not try to delete ttfontdata + global $pdf_tmp_dir; + $this->pdf_tmp_dir = $pdf_tmp_dir ? $pdf_tmp_dir : "/tmp"; + $mpdf_temp = $this->pdf_tmp_dir . "/mpdf/temp/"; + $mpdf_font_data = $this->pdf_tmp_dir . "/mpdf/ttfontdata/"; + + if (!is_dir($mpdf_temp)) { + mkdir($mpdf_temp, 0775, true); } - # ------------------------------------------------------------------------ - # Shipment Labels - function _get_manifest() { - if (!$this->user->has('view_manifest')) $this->_error('Access denied'); + if (!is_dir($mpdf_font_data)) { + mkdir($mpdf_font_data, 0775, true); + } + } + + # ------------------------------------------------------------------------ + # Shipment Labels + function _get_manifest() + { + if (!$this->user->hasPermission('view_manifest')) + $this->_error('Access denied'); - $month = $this->has_arg('month') ? $this->arg('month') : date('m-Y'); + $month = $this->has_arg('month') ? $this->arg('month') : date('m-Y'); - $d = new \DateTime('01-'.$month); - $d->modify('first day of this month'); - $start = $d->format('d-m-Y'); - $d->modify('last day of this month'); - $end = $d->format('d-m-Y'); + $d = new \DateTime('01-' . $month); + $d->modify('first day of this month'); + $start = $d->format('d-m-Y'); + $d->modify('last day of this month'); + $end = $d->format('d-m-Y'); - $this->period = $start.' to '.$end; + $this->period = $start . ' to ' . $end; - $shipments = $this->db->pq("SELECT s.shippingname, s.shippingid, s.deliveryagent_agentcode, s.deliveryagent_productcode, IF(cta.couriertermsacceptedid,1,0) as termsaccepted, GROUP_CONCAT(d.deliveryagent_barcode) as deliveryagent_barcode, s.deliveryagent_flightcode, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as deliveryagent_shippingdate, TO_CHAR(s.deliveryagent_flightcodetimestamp, 'HH24:MI
DD-MM-YYYY') as deliveryagent_flightcodetimestamp, count(distinct d.dewarid) as dewarcount, sum(d.weight) as weight, pe.givenname, pe.familyname, l.name as labname, l.address, l.city, l.postcode, l.country, CONCAT(p.proposalcode, p.proposalnumber) as prop, GROUP_CONCAT(IF(d.facilitycode, d.facilitycode, d.code)) as dewars + $shipments = $this->db->pq("SELECT s.shippingname, s.shippingid, s.deliveryagent_agentcode, s.deliveryagent_productcode, IF(cta.couriertermsacceptedid,1,0) as termsaccepted, GROUP_CONCAT(d.deliveryagent_barcode) as deliveryagent_barcode, s.deliveryagent_flightcode, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as deliveryagent_shippingdate, TO_CHAR(s.deliveryagent_flightcodetimestamp, 'HH24:MI
DD-MM-YYYY') as deliveryagent_flightcodetimestamp, count(distinct d.dewarid) as dewarcount, sum(d.weight) as weight, pe.givenname, pe.familyname, l.name as labname, l.address, l.city, l.postcode, l.country, CONCAT(p.proposalcode, p.proposalnumber) as prop, GROUP_CONCAT(IF(d.facilitycode, d.facilitycode, d.code)) as dewars FROM shipping s INNER JOIN dewar d ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid @@ -86,36 +87,39 @@ function _get_manifest() { AND s.deliveryagent_shippingdate <= TO_DATE(:2, 'DD-MM-YYYY') GROUP BY s.shippingid", array($start, $end)); - $totals = array('WEIGHT' => 0, 'PIECES' => 0, 'SHIPMENTS' => 0); - foreach ($shipments as &$s) { - $totals['WEIGHT'] += $s['WEIGHT']; - $totals['PIECES'] += $s['DEWARCOUNT']; - $totals['SHIPMENTS']++; + $totals = array('WEIGHT' => 0, 'PIECES' => 0, 'SHIPMENTS' => 0); + foreach ($shipments as &$s) { + $totals['WEIGHT'] += $s['WEIGHT']; + $totals['PIECES'] += $s['DEWARCOUNT']; + $totals['SHIPMENTS']++; - $s['DELIVERYAGENT_BARCODE'] = str_replace(',', '
', $s['DELIVERYAGENT_BARCODE']); - $s['DEWARS'] = str_replace(',', '
', $s['DEWARS']); - } + $s['DELIVERYAGENT_BARCODE'] = str_replace(',', '
', $s['DELIVERYAGENT_BARCODE']); + $s['DEWARS'] = str_replace(',', '
', $s['DEWARS']); + } - $this->shipments = $shipments; - $this->totals = $totals; + $this->shipments = $shipments; + $this->totals = $totals; - global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_phone, $dhl_acc; - $addr = array(str_replace("\n", '
', $facility_address), $facility_city, $facility_postcode, $facility_country, $facility_phone); - $this->facility = $facility_company; - $this->facility_address = implode("
", $addr); - $this->facility_account = $dhl_acc; + global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_phone, $dhl_acc; + $addr = array(str_replace("\n", '
', $facility_address), $facility_city, $facility_postcode, $facility_country, $facility_phone); + $this->facility = $facility_company; + $this->facility_address = implode("
", $addr); + $this->facility_account = $dhl_acc; + + $this->_render('shipping_manifest', 'L'); + } - $this->_render('shipping_manifest', 'L'); - } - - # ------------------------------------------------------------------------ - # Shipment Labels - function _shipment_label() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified', 'Please select a proposal first'); - if (!$this->has_arg('sid')) $this->_error('No shipment specified', 'No shipment id was specified'); - - $ship = $this->db->pq("SELECT s.safetylevel, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.shippingid, s.shippingname, pe.givenname, pe.familyname, pe.phonenumber,pe.faxnumber, l.name as labname, l.address, l.city, l.postcode, l.country, pe2.givenname as givenname2, pe2.familyname as familyname2, pe2.phonenumber as phonenumber2, pe2.faxnumber as faxnumber2, l2.name as labname2, l2.address as address2, l2.city as city2, l2.postcode as postcode2, l2.country as country2, c2.courieraccount, c2.billingreference, c2.defaultcourriercompany + # ------------------------------------------------------------------------ + # Shipment Labels + function _shipment_label() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified', 'Please select a proposal first'); + if (!$this->has_arg('sid')) + $this->_error('No shipment specified', 'No shipment id was specified'); + + $ship = $this->db->pq("SELECT s.safetylevel, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.shippingid, s.shippingname, pe.givenname, pe.familyname, pe.phonenumber,pe.faxnumber, l.name as labname, l.address, l.city, l.postcode, l.country, pe2.givenname as givenname2, pe2.familyname as familyname2, pe2.phonenumber as phonenumber2, pe2.faxnumber as faxnumber2, l2.name as labname2, l2.address as address2, l2.city as city2, l2.postcode as postcode2, l2.country as country2, c2.courieraccount, c2.billingreference, c2.defaultcourriercompany FROM shipping s INNER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid INNER JOIN person pe ON c.personid = pe.personid @@ -125,28 +129,37 @@ function _shipment_label() { INNER JOIN laboratory l2 ON l2.laboratoryid = pe2.laboratoryid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE s.shippingid=:1", array($this->arg('sid'))); - if (!sizeof($ship)) $this->_error('No such shipment', 'The specified shipment doesnt exist'); - else $ship = $ship[0]; - - $addr = array($ship['ADDRESS']); - if ($ship['CITY']) array_push($addr, $ship['CITY']."\n"); - if ($ship['POSTCODE']) array_push($addr, $ship['POSTCODE']."\n"); - if ($ship['COUNTRY']) array_push($addr, $ship['COUNTRY']."\n"); - $ship['ADDRESS'] = str_replace("\n", '
', implode('', $addr)); - - $addr = array($ship['ADDRESS2']); - if ($ship['CITY2']) array_push($addr, $ship['CITY2']."\n"); - if ($ship['POSTCODE2']) array_push($addr, $ship['POSTCODE2']."\n"); - if ($ship['COUNTRY2']) array_push($addr, $ship['COUNTRY2']."\n"); - $ship['ADDRESS2'] = str_replace("\n", '
', implode('', $addr)); - - global $facility_fao, $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_phone; - $addr = array($facility_fao, $facility_company, str_replace("\n", '
', $facility_address), $facility_city, $facility_postcode, $facility_country, $facility_phone); - $ship['FACILITYADDRESS'] = implode("
", $addr); - - $this->ship = $ship; - - $this->dewars = $this->db->pq("SELECT bl.beamlinename, bl.beamlineoperator, TO_CHAR(bl.startdate, 'DD-MM-YYYY') as st, d.transportvalue, d.customsvalue, d.code, d.barcode, count(cq.containerqueueid) as auto, count(c.containerid) as containers, GROUP_CONCAT(COALESCE(cr.barCode, c.code) LIMIT 10) as containersBarCode + + if (!sizeof($ship)) + $this->_error('No such shipment', 'The specified shipment doesnt exist'); + else + $ship = $ship[0]; + + $addr = array(rtrim($ship['ADDRESS'])); + if ($ship['CITY']) + array_push($addr, PHP_EOL . $ship['CITY']); + if ($ship['POSTCODE']) + array_push($addr, PHP_EOL . $ship['POSTCODE']); + if ($ship['COUNTRY']) + array_push($addr, PHP_EOL . $ship['COUNTRY'] . PHP_EOL); + $ship['ADDRESS'] = str_replace(PHP_EOL, '
', implode('', $addr)); + + $addr = array(rtrim($ship['ADDRESS2'])); + if ($ship['CITY2']) + array_push($addr, PHP_EOL . $ship['CITY2']); + if ($ship['POSTCODE2']) + array_push($addr, PHP_EOL . $ship['POSTCODE2']); + if ($ship['COUNTRY2']) + array_push($addr, PHP_EOL . $ship['COUNTRY2'] . PHP_EOL); + $ship['ADDRESS2'] = str_replace(PHP_EOL, '
', implode('', $addr)); + + global $facility_fao, $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_phone; + $addr = array($facility_fao, $facility_company, str_replace("\n", '
', $facility_address), $facility_city, $facility_postcode, $facility_country, $facility_phone); + $ship['FACILITYADDRESS'] = implode("
", $addr); + + $this->ship = $ship; + + $this->dewars = $this->db->pq("SELECT bl.beamlinename, bl.beamlineoperator, TO_CHAR(bl.startdate, 'DD-MM-YYYY') as st, d.transportvalue, d.customsvalue, d.code, d.barcode, count(cq.containerqueueid) as auto, count(c.containerid) as containers, GROUP_CONCAT(COALESCE(cr.barCode, c.code) LIMIT 10) as containersBarCode FROM dewar d LEFT OUTER JOIN blsession bl ON d.firstexperimentid = bl.sessionid LEFT OUTER JOIN container c ON c.dewarid = d.dewarid @@ -154,199 +167,214 @@ function _shipment_label() { LEFT OUTER JOIN containerqueue cq ON c.containerid = cq.containerid WHERE d.shippingid=:1 GROUP BY d.dewarid", array($ship['SHIPPINGID'])); - - $this->_render('shipment_label'); - } + $this->_render('shipment_label'); + } - function _get_awb() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipping id specified'); - - $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_label + + function _get_awb() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); + + $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_label FROM shipping s - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - - if (!sizeof($ship)) $this->_error('No such shipment'); - - if ($ship[0]['DELIVERYAGENT_LABEL']) { - $this->app->contentType('application/pdf'); - echo base64_decode($ship[0]['DELIVERYAGENT_LABEL']); - } else $this->_error('No airway bill for this shipment'); + WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); + + if (!sizeof($ship)) + $this->_error('No such shipment'); + + if ($ship[0]['DELIVERYAGENT_LABEL']) { + $this->app->contentType('application/pdf'); + echo base64_decode($ship[0]['DELIVERYAGENT_LABEL']); + } else + $this->_error('No airway bill for this shipment'); + } + + + # ------------------------------------------------------------------------ + # Report of Data Collections + function _visit_report() + { + if (!$this->has_arg('visit')) { + $this->_error('No visit specified', 'You need to specify a visit to view this page'); } - - - # ------------------------------------------------------------------------ - # Report of Data Collections - function _visit_report() { - if (!$this->has_arg('visit')) $this->_error('No visit specified', 'You need to specify a visit to view this page'); - - $info = $this->db->pq("SELECT TIMESTAMPDIFF('MINUTE', s.startdate, s.enddate)/60 as len, s.sessionid as sid, s.beamlinename, s.beamlineoperator as lc, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, CONCAT(p.proposalcode, p.proposalnumber) as prop + + $info = $this->db->pq("SELECT TIMESTAMPDIFF('MINUTE', s.startdate, s.enddate)/60 as len, s.sessionid as sid, s.beamlinename, s.beamlineoperator as lc, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, CONCAT(p.proposalcode, p.proposalnumber) as prop FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1", array($this->arg('visit'))); - - if (!sizeof($info)) $this->_error('No such visit', 'The specified visit doesnt exist'); - else $info = $info[0]; - - $this->info = $info; - - $this->users = $this->db->pq("SELECT p.title, p.familyname, p.givenname FROM person p + WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1", array($this->arg('visit'))); + + if (!sizeof($info)) + $this->_error('No such visit', 'The specified visit doesnt exist'); + else + $info = $info[0]; + + $this->info = $info; + + $this->users = $this->db->pq("SELECT p.title, p.familyname, p.givenname FROM person p INNER JOIN session_has_person shp ON p.personid = shp.personid WHERE shp.sessionid=:1", array($info['SID'])); - - $rows = $this->db->pq("SELECT dc.datacollectionid as id, dc.overlap,dc.imageprefix,dc.imagedirectory as dir,dc.datacollectionnumber,TO_CHAR(dc.starttime, 'DD/MM/YYYY HH24:MI:SS'), sa.name, p.name as protein, dc.numberofimages, dc.wavelength, dc.detectordistance, dc.exposuretime, dc.axisstart, dc.axisrange, dc.xbeam, dc.ybeam, dc.resolution, dc.comments + + $rows = $this->db->pq("SELECT dc.datacollectionid as id, dc.overlap,dc.imageprefix,dc.imagedirectory as dir,dc.datacollectionnumber,TO_CHAR(dc.starttime, 'DD/MM/YYYY HH24:MI:SS'), sa.name, p.name as protein, dc.numberofimages, dc.wavelength, dc.detectordistance, dc.exposuretime, dc.axisstart, dc.axisrange, dc.xbeam, dc.ybeam, dc.resolution, dc.comments FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid LEFT OUTER JOIN blsample sa ON dc.blsampleid = sa.blsampleid LEFT OUTER JOIN crystal c ON sa.crystalid = c.crystalid LEFT OUTER JOIN protein p ON c.proteinid = p.proteinid WHERE dcg.sessionid=:1 ORDER BY dc.starttime", array($info['SID'])); - - if (!sizeof($rows)) $this->_error('No data', 'No data collections for this visit yet'); - - $screen = array(); - $dcs = array(); - foreach ($rows as $dc) { - if ($dc['OVERLAP'] == 0) array_push($dcs, 'api.datacollectionid='.$dc['ID']); - else array_push($screen, 'datacollectionid='.$dc['ID']); - } - - $screen = implode(' OR ', $screen); - $dcs = implode(' OR ', $dcs); - - $aps = sizeof($dcs) ? $this->db->pq("SELECT api.datacollectionid as id, app.autoprocprogramid,app.processingcommandline as type, apss.ntotalobservations as ntobs, apss.ntotaluniqueobservations as nuobs, apss.resolutionlimitlow as rlow, apss.resolutionlimithigh as rhigh, apss.scalingstatisticstype as shell, apss.rmeasalliplusiminus as rmeas, apss.rmerge, apss.completeness, apss.anomalouscompleteness as anomcompleteness, apss.anomalousmultiplicity as anommultiplicity, apss.multiplicity, apss.meanioversigi as isigi, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, ap.refinedcell_c as cell_c, ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga + + if (!sizeof($rows)) { + $this->_error('No data', 'No data collections for this visit yet'); + } + + $dcs = array(); + foreach ($rows as $dc) { + if ($dc['OVERLAP'] == 0) + array_push($dcs, 'api.datacollectionid=' . $dc['ID']); + } + + $dcs_clause = implode(' OR ', $dcs); + + $aps = sizeof($dcs) ? $this->db->pq("SELECT api.datacollectionid as id, app.autoprocprogramid,app.processingcommandline as type, apss.ntotalobservations as ntobs, apss.ntotaluniqueobservations as nuobs, apss.resolutionlimitlow as rlow, apss.resolutionlimithigh as rhigh, apss.scalingstatisticstype as shell, apss.rmeasalliplusiminus as rmeas, apss.rmerge, apss.completeness, apss.anomalouscompleteness as anomcompleteness, apss.anomalousmultiplicity as anommultiplicity, apss.multiplicity, apss.meanioversigi as isigi, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, ap.refinedcell_c as cell_c, ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga FROM autoprocintegration api INNER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid INNER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid INNER JOIN autoproc ap ON aps.autoprocid = ap.autoprocid INNER JOIN autoprocscalingstatistics apss ON apss.autoprocscalingid = aph.autoprocscalingid INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid - WHERE $dcs") : array(); - - $types = array('fast_dp' => 1, '-3da ' => 3, '-2da ' => 2, '-3daii ' => 4); - $dts = array('cell_a', 'cell_b', 'cell_c', 'cell_al', 'cell_be', 'cell_ga'); - $dts2 = array('rlow', 'rhigh'); - - $ap = array(); - foreach ($aps as &$a) { - foreach ($types as $id => $name) { - if (strpos($a['TYPE'], $id)) { - $a['TYPE'] = $name; - break; - } - } - - foreach ($dts as $d) { - $a[strtoupper($d)] = number_format($a[strtoupper($d)], 2); + WHERE $dcs_clause") : array(); + + $types = array('fast_dp' => 1, '-3da ' => 3, '-2da ' => 2, '-3daii ' => 4); + $dts = array('cell_a', 'cell_b', 'cell_c', 'cell_al', 'cell_be', 'cell_ga'); + $dts2 = array('rlow', 'rhigh'); + + $ap = array(); + foreach ($aps as &$a) { + foreach ($types as $id => $name) { + if (strpos($a['TYPE'], $id)) { + $a['TYPE'] = $name; + break; } - - - if (!array_key_exists($a['ID'], $ap)) $ap[$a['ID']] = array(); - if (!array_key_exists($a['TYPE'], $ap[$a['ID']])) $ap[$a['ID']][$a['TYPE']] = array(); - - $a['ISIGI'] = number_format($a['ISIGI'], 1); - $a['RMEAS'] = number_format($a['RMEAS'], 3); - $a['COMPLETENESS'] = number_format($a['COMPLETENESS'], 1); - - $ap[$a['ID']][$a['TYPE']][$a['SHELL']] = $a; } - foreach ($rows as &$dc) { - if (array_key_exists($dc['ID'], $ap)) { - $a = $ap[$dc['ID']][max(array_keys($ap[$dc['ID']]))]; - - $o = $a['overall']; - $dc['SG'] = $o['SG']; - $dc['CELL'] = $o['CELL_A'].', '.$o['CELL_B'].', '.$o['CELL_C'].'
'.$o['CELL_AL'].', '.$o['CELL_BE'].', '.$o['CELL_GA']; - - $dc['AP'] = array($o,$a['innerShell'],$a['outerShell']); - - } else { - $dc['SG'] = ''; - $dc['CELL'] = ''; - $dc['AP'] = array(); - } - - $dc['DIR'] = preg_replace('/.*\/\d\d\d\d\/\w\w\d+-\d+\//', '', $dc['DIR']); + foreach ($dts as $d) { + $a[strtoupper($d)] = number_format($a[strtoupper($d)], 2); + } + + if (!array_key_exists($a['ID'], $ap)) + $ap[$a['ID']] = array(); + if (!array_key_exists($a['TYPE'], $ap[$a['ID']])) + $ap[$a['ID']][$a['TYPE']] = array(); + + $a['ISIGI'] = number_format($a['ISIGI'], 1); + $a['RMEAS'] = number_format($a['RMEAS'], 3); + $a['COMPLETENESS'] = number_format($a['COMPLETENESS'], 1); + + $ap[$a['ID']][$a['TYPE']][$a['SHELL']] = $a; + } + + foreach ($rows as &$dc) { + if (array_key_exists($dc['ID'], $ap)) { + $a = $ap[$dc['ID']][max(array_keys($ap[$dc['ID']]))]; + + $o = $a['overall']; + $dc['SG'] = $o['SG']; + $dc['CELL'] = $o['CELL_A'] . ', ' . $o['CELL_B'] . ', ' . $o['CELL_C'] . '
' . $o['CELL_AL'] . ', ' . $o['CELL_BE'] . ', ' . $o['CELL_GA']; + + $dc['AP'] = array($o, $a['innerShell'], $a['outerShell']); + } else { + $dc['SG'] = ''; + $dc['CELL'] = ''; + $dc['AP'] = array(); } - - $this->dcs = $rows; - - $rows = $this->db->pq("SELECT sa.name, p.name as protein, es.element, es.peakfprime, es.exposuretime, es.peakfdoubleprime, TO_CHAR(es.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, es.transmissionfactor as transmission, es.inflectionfprime, es.inflectionfdoubleprime, es.comments, es.peakenergy, es.inflectionenergy + + $dc['DIR'] = preg_replace('/.*\/\d\d\d\d\/\w\w\d+-\d+\//', '', $dc['DIR']); + } + + $this->dcs = $rows; + + $rows = $this->db->pq("SELECT sa.name, p.name as protein, es.element, es.peakfprime, es.exposuretime, es.peakfdoubleprime, TO_CHAR(es.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, es.transmissionfactor as transmission, es.inflectionfprime, es.inflectionfdoubleprime, es.comments, es.peakenergy, es.inflectionenergy FROM energyscan es LEFT OUTER JOIN blsample sa ON es.blsampleid = sa.blsampleid LEFT OUTER JOIN crystal c ON sa.crystalid = c.crystalid LEFT OUTER JOIN protein p ON c.proteinid = p.proteinid WHERE es.sessionid=:1 ORDER BY es.starttime", array($info['SID'])); - - foreach ($rows as &$r) $r['TRANSMISSION']*= 100; - - $this->ess = $rows; - - $rows = $this->db->pq("SELECT sa.name, p.name as protein, xrf.filename as dir, xrf.exposuretime, TO_CHAR(xrf.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, xrf.beamtransmission as transmission, xrf.energy, xrf.comments + + foreach ($rows as &$r) + $r['TRANSMISSION'] *= 100; + + $this->ess = $rows; + + $rows = $this->db->pq("SELECT sa.name, p.name as protein, xrf.filename as dir, xrf.exposuretime, TO_CHAR(xrf.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, xrf.beamtransmission as transmission, xrf.energy, xrf.comments FROM xfefluorescencespectrum xrf LEFT OUTER JOIN blsample sa ON xrf.blsampleid = sa.blsampleid LEFT OUTER JOIN crystal c ON sa.crystalid = c.crystalid LEFT OUTER JOIN protein p ON c.proteinid = p.proteinid WHERE xrf.sessionid=:1 ORDER BY xrf.starttime", array($info['SID'])); - - foreach ($rows as &$r) { - $results = str_replace('.mca', '.results.dat', preg_replace('/(data\/\d\d\d\d\/\w\w\d+-\d+)/', '\1/processed/pymca', $r['DIR'])); - - $elements = array(); - if (file_exists($results)) { - $dat = explode("\n",file_get_contents($results)); - foreach ($dat as $i => $d) { - $l = explode(' ', $d); - if ($i < 5) array_push($elements, $l[0]); - } + + foreach ($rows as &$r) { + $results = str_replace('.mca', '.results.dat', preg_replace('/(data\/\d\d\d\d\/\w\w\d+-\d+)/', '\1/processed/pymca', $r['DIR'])); + + $elements = array(); + if (file_exists($results)) { + $dat = explode("\n", file_get_contents($results)); + foreach ($dat as $i => $d) { + $l = explode(' ', $d); + if ($i < 5) + array_push($elements, $l[0]); } - - $r['ELEMENTS'] = $elements; } - $this->fls = $rows; + $r['ELEMENTS'] = $elements; + } + + $this->fls = $rows; + + // Generate stats here by consuming REST API internally. + + $this->_render('visit_report', 'L'); + } + + + # ------------------------------------------------------------------------ + # Report of containers from a dewar + function _container_report() + { + if (!($this->has_arg('cid') || $this->has_arg('did') || $this->has_arg('sid'))) + $this->_error('No container, dewar, or shipment', 'No container, dewar, or shipment was specified'); + if (!$this->has_arg('prop')) + $this->_error('No proposal specified', 'No proposal was specified'); - // Generate stats here by consuming REST API internally. - - $this->_render('visit_report', 'L'); + if ($this->has_arg('cid')) { + $where = 'c.containerid=:2'; + $args = $this->arg('cid'); } - - - # ------------------------------------------------------------------------ - # Report of containers from a dewar - function _container_report() { - if (!($this->has_arg('cid') || $this->has_arg('did') || $this->has_arg('sid'))) $this->_error('No container, dewar, or shipment', 'No container, dewar, or shipment was specified'); - if (!$this->has_arg('prop')) $this->_error('No proposal specified', 'No proposal was specified'); - - if ($this->has_arg('cid')) { - $where = 'c.containerid=:2'; - $args = $this->arg('cid'); - } - - if ($this->has_arg('did')) { - $where = 'd.dewarid=:2'; - $args = $this->arg('did'); - } - - if ($this->has_arg('sid')) { - $where = 's.shippingid=:2'; - $args = $this->arg('sid'); - } - - - $containers = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, c.capacity, c.containerid,c.code as container, d.code as dewar, s.shippingname as shipment + + if ($this->has_arg('did')) { + $where = 'd.dewarid=:2'; + $args = $this->arg('did'); + } + + if ($this->has_arg('sid')) { + $where = 's.shippingid=:2'; + $args = $this->arg('sid'); + } + + + $containers = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, c.capacity, c.containerid,c.code as container, d.code as dewar, s.shippingname as shipment FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalid=:1 AND $where", array($this->proposalid, $args)); - - - foreach ($containers as &$c) { - $c['SAMPLES'] = $this->db->pq("SELECT sp.code, sp.comments, sp.name, TO_NUMBER(sp.location) as location, pr.acronym, cr.spacegroup + + + foreach ($containers as &$c) { + $c['SAMPLES'] = $this->db->pq("SELECT sp.code, sp.comments, sp.name, TO_NUMBER(sp.location) as location, pr.acronym, cr.spacegroup FROM blsample sp INNER JOIN crystal cr ON sp.crystalid = cr.crystalid INNER JOIN protein pr ON cr.proteinid = pr.proteinid @@ -354,86 +382,96 @@ function _container_report() { INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid WHERE pr.proposalid=:1 AND c.containerid = :2 - ORDER BY TO_NUMBER(sp.location)", array($this->proposalid,$c['CONTAINERID'])); - - $used = array(); - foreach($c['SAMPLES'] as $r) array_push($used, $r['LOCATION']); - $tot = array(); - for ($i = 1; $i < $c['CAPACITY']+1; $i++) array_push($tot, $i); - - foreach (array_diff($tot, $used) as $i => $d) { - array_splice($c['SAMPLES'], $d-1, 0, array(array('COMMENTS' => '', 'NAME' => '', 'LOCATION' => $d, 'ACRONYM' => '', 'SPACEGROUP' => '', 'CODE' => ''))); - } + ORDER BY TO_NUMBER(sp.location)", array($this->proposalid, $c['CONTAINERID'])); + + $used = array(); + foreach ($c['SAMPLES'] as $r) + array_push($used, $r['LOCATION']); + $tot = array(); + for ($i = 1; $i < $c['CAPACITY'] + 1; $i++) + array_push($tot, $i); + + foreach (array_diff($tot, $used) as $i => $d) { + array_splice($c['SAMPLES'], $d - 1, 0, array(array('COMMENTS' => '', 'NAME' => '', 'LOCATION' => $d, 'ACRONYM' => '', 'SPACEGROUP' => '', 'CODE' => ''))); } - - $this->containers = $containers; - $this->_render('container', 'L'); } - - - # ------------------------------------------------------------------------ - # Generate barcodes for labels - function _generate_sheets() { - $this->labels = array('i03', 'mx-storage-i03', 'i03-rack', 'mx-storage-i02', 'mx-storage-i04', 'mx-storage-i04-1', 'mx-storage-i24', 'mx-storage-in-in','mx-storage-in-out'); - - if ($this->has_arg('labels')) { - if (is_array($this->arg('labels'))) $this->labels = $this->arg('labels'); - else $this->labels = array($this->arg('labels')); - } - $this->_render('sheets', 'L'); + $this->containers = $containers; + $this->_render('container', 'L'); + } + + # ------------------------------------------------------------------------ + # Generate barcodes for labels + function _generate_sheets() + { + $this->labels = array('i03', 'mx-storage-i03', 'i03-rack', 'mx-storage-i02', 'mx-storage-i04', 'mx-storage-i04-1', 'mx-storage-i24', 'mx-storage-in-in', 'mx-storage-in-out'); + if ($this->has_arg('labels')) { + if (is_array($this->arg('labels'))) + $this->labels = $this->arg('labels'); + else + $this->labels = array($this->arg('labels')); } - - # ------------------------------------------------------------------------ - # Render html template to PDF file - function _render($file, $orientation = '') { - ini_set('memory_limit', '256M'); - - $f = 'assets/pdf/'.$file.'.php'; - - if (!$this->has_arg('p')) { - if ($orientation) $orientation = '-'.$orientation; - - # Enable output buffering to capture html - ob_start(); - - $mpdf = new mPDF('', 'A4'.$orientation); - $mpdf->WriteHTML(file_get_contents('assets/pdf/styles.css'),1); - - if (file_exists($f)) { - extract($this->vars); - include($f); - } + $this->_render('sheets', 'L'); + } - # Write output buffer to pdf - $mpdf->WriteHTML(ob_get_contents()); - ob_end_clean(); + # ------------------------------------------------------------------------ + # Render html template to PDF file + function _render($file, $orientation = '') + { + ini_set('memory_limit', '256M'); - $this->app->contentType('application/pdf'); - $mpdf->Output(); - - - # Preview mode outputs raw html, add /p/1 to url - } else { - print "\n \n \n \n \n\n"; + $f = 'assets/pdf/' . $file . '.php'; + + if (!$this->has_arg('p')) { + if ($orientation) + $orientation = '-' . $orientation; + + # Enable output buffering to capture html + ob_start(); - if (file_exists($f)) { - extract($this->vars); - include($f); - } - print "\n\n \n"; + $mpdf = new Mpdf([ + 'mode' => 'utf-8', + 'format' => 'A4' . $orientation, + 'tempDir' => $this->pdf_tmp_dir + ]); + $mpdf->WriteHTML(file_get_contents('assets/pdf/styles.css'), 1); + + if (file_exists($f)) { + extract($this->vars); + include($f); } - } - function __get($name) { - return $this->vars[$name]; - } - - function __set($name, $value) { - $this->vars[$name] = $value; + # Write output buffer to pdf + $mpdf->WriteHTML(ob_get_contents()); + ob_end_clean(); + + $this->app->contentType('application/pdf'); + $mpdf->Output(); + + # Preview mode outputs raw html, add /p/1 to url + } else { + print "\n \n \n \n \n\n"; + + if (file_exists($f)) { + extract($this->vars); + include($f); + } + + print "\n\n \n"; } + } + + function __get($name) + { + return $this->vars[$name]; + } + + function __set($name, $value) + { + $this->vars[$name] = $value; + } } diff --git a/api/src/Page/Process.php b/api/src/Page/Process.php index 10af45559..70fadb66d 100644 --- a/api/src/Page/Process.php +++ b/api/src/Page/Process.php @@ -11,7 +11,7 @@ class Process extends Page public static $arg_list = array( 'PROCESSINGJOBID' => '\d+', 'DATACOLLECTIONID' => '\d+', - 'DISPLAYNAME' => '([\w\s-])+', + 'DISPLAYNAME' => '([\w\s\-])+', 'COMMENTS' => '.*', 'PROCESSINGJOBIMAGESWEEPID' => '\d+', @@ -21,16 +21,16 @@ class Process extends Page 'PROCESSINGJOBPARAMETERID' => '\d+', 'PARAMETERKEY' => '\w+', // Processing parameter value needs to cope with '/' characters e.g. spacegroups for reprocessing - 'PARAMETERVALUE' => '([a-zA-Z0-9-_\.,\/])+', + 'PARAMETERVALUE' => '([a-zA-Z0-9\-_\.,\/])+', 'AUTOMATIC' => '\d', - 'RECIPE' => '([\w-])+', + 'RECIPE' => '([\w\-])+', 'ids' => '\d+', - 'pipelinestatus' => '([\w\s-])+', - 'category' => '([\w\s-])+', - 'discipline' => '([\w\s-])+', + 'pipelinestatus' => '([\w\s\-])+', + 'category' => '([\w\s\-])+', + 'discipline' => '([\w\s\-])+', ); diff --git a/api/src/Page/Processing.php b/api/src/Page/Processing.php index 9dceae7c6..20546718c 100644 --- a/api/src/Page/Processing.php +++ b/api/src/Page/Processing.php @@ -20,6 +20,11 @@ class Processing extends Page { 'get', '_downstream_mapmodel', ), + array( + '/multiplex_jobs/groups/:blSampleGroupId', + 'get', + '_get_latest_multiplex_job_result' + ), ); public static $arg_list = array( @@ -28,6 +33,8 @@ class Processing extends Page { 'visit' => '\w+\d+-\d+', 'map' => '\d+', 'n' => '\d+', + 'sampleGroupId' => '\d+', + 'resultCount' => '\d+' ); /** @@ -80,11 +87,11 @@ function _screening_status($where, $ids) { function _xrc_status($where, $ids) { $dcs = $this->db->pq( - "SELECT xrc.status as xrcstatus, dc.datacollectionid + "SELECT xrc.status as xrcstatus, dc.datacollectionid, xrc.xrayCentringType as method, xrcr.xraycentringresultid as xrcresults FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid - INNER JOIN gridinfo gr ON (gr.datacollectionid = dc.datacollectionid OR gr.datacollectiongroupid = dc.datacollectiongroupid) - LEFT OUTER JOIN xraycentringresult xrc ON xrc.gridinfoid = gr.gridinfoid + LEFT OUTER JOIN xraycentring xrc ON xrc.datacollectiongroupid = dc.datacollectiongroupid + LEFT OUTER JOIN XrayCentringResult xrcr ON xrc.xrayCentringId = xrcr.xrayCentringId INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", @@ -98,13 +105,15 @@ function _xrc_status($where, $ids) { } $statuses[$dc['DATACOLLECTIONID']]['XrayCentring'] = - $dc['XRCSTATUS'] === null - ? 0 - : ($dc['XRCSTATUS'] === 'pending' - ? 1 - : ($dc['XRCSTATUS'] === 'success' - ? 2 - : 3)); + $dc['METHOD'] !== '3d' + ? -1 + : ($dc['XRCSTATUS'] === null + ? 0 + : ($dc['XRCSTATUS'] === 'pending' + ? 1 + : ($dc['XRCSTATUS'] === 'success' && $dc['XRCRESULTS'] !== null + ? 2 + : 3))); } return $statuses; @@ -235,156 +244,16 @@ function _statuses() { * @param integer $id DataCollectionId */ function _results($id) { - $rows = $this->db->pq( - 'SELECT apss.cchalf, apss.ccanomalous, apss.anomalous, dc.xbeam, dc.ybeam, api.refinedxbeam, api.refinedybeam, app.autoprocprogramid,app.processingprograms as type, apss.ntotalobservations as ntobs, apss.ntotaluniqueobservations as nuobs, apss.resolutionlimitlow as rlow, apss.resolutionlimithigh as rhigh, apss.scalingstatisticstype as shell, apss.rmeasalliplusiminus as rmeas, apss.rmerge, apss.completeness, apss.anomalouscompleteness as anomcompleteness, apss.anomalousmultiplicity as anommultiplicity, apss.multiplicity, apss.meanioversigi as isigi, ap.spacegroup as sg, ap.refinedcell_a as cell_a, ap.refinedcell_b as cell_b, ap.refinedcell_c as cell_c, ap.refinedcell_alpha as cell_al, ap.refinedcell_beta as cell_be, ap.refinedcell_gamma as cell_ga, - (SELECT COUNT(api1.autoprocintegrationid) FROM autoprocintegration api1 WHERE api1.autoprocprogramid = app.autoprocprogramid) as imagesweepcount, app.processingstatus, app.processingmessage, count(distinct pjis.datacollectionid) as dccount, max(pjis.processingjobid) as processingjobid, pj.automatic - FROM autoprocintegration api - LEFT OUTER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid - LEFT OUTER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid - LEFT OUTER JOIN autoproc ap ON aps.autoprocid = ap.autoprocid - LEFT OUTER JOIN autoprocscalingstatistics apss ON apss.autoprocscalingid = aph.autoprocscalingid - INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid - LEFT OUTER JOIN processingjob pj ON app.processingjobid = pj.processingjobid - LEFT OUTER JOIN processingjobimagesweep pjis ON pjis.processingjobid = pj.processingjobid - INNER JOIN datacollection dc ON api.datacollectionid = dc.datacollectionid - WHERE api.datacollectionid = :1 AND app.processingstatus IS NOT NULL - GROUP BY app.autoprocprogramid, apss.autoprocscalingstatisticsid - ORDER BY apss.scalingstatisticstype DESC', - array($id) - ); - - $msg_tmp = $this->db->pq( - "SELECT api.autoprocprogramid, appm.recordtimestamp, appm.severity, appm.message, appm.description - FROM autoprocprogrammessage appm - INNER JOIN autoprocintegration api ON api.autoprocprogramid = appm.autoprocprogramid - WHERE api.datacollectionid=:1", - array($id) - ); - - $messages = array(); - foreach ($msg_tmp as $m) { - if (!array_key_exists($m['AUTOPROCPROGRAMID'], $messages)) { - $messages[$m['AUTOPROCPROGRAMID']] = array(); - } - array_push($messages[$m['AUTOPROCPROGRAMID']], $m); - } - - $dts = array( - 'cell_a', - 'cell_b', - 'cell_c', - 'cell_al', - 'cell_be', - 'cell_ga', - ); - $dts2 = array('rlow', 'rhigh'); - - $output = array(); - foreach ($rows as &$r) { - if (!array_key_exists($r['AUTOPROCPROGRAMID'], $output)) { - $output[$r['AUTOPROCPROGRAMID']] = array( - 'BEAM' => array(), - 'SHELLS' => array(), - 'CELL' => array(), - ); - } - - if ($r['PROCESSINGSTATUS'] == '1') { - $shell = array(); - foreach ($r as $k => &$v) { - if ($k == 'TYPE') { - if ($r['DCCOUNT'] > 1) { - $prefix = preg_match('/multi/', $v) ? '' : 'multi-'; - $v = $r['DCCOUNT'] . 'x ' . $prefix . $v; - } - } - - if ($k == 'ISIGI') { - $v = number_format($v, 1); - } - if ($k == 'RMERGE') { - $v = number_format($v, 3); - } - if ($k == 'RMEAS') { - $v = number_format($v, 3); - } - if ($k == 'COMPLETENESS') { - $v = number_format($v, 1); - } - if ($k == 'MULTIPLICITY') { - $v = number_format($v, 1); - } - if ($k == 'ANOMCOMPLETENESS') { - $v = number_format($v, 1); - } - if ($k == 'ANOMMULTIPLICITY') { - $v = number_format($v, 1); - } - if ($k == 'CCHALF') { - $v = number_format($v, 1); - } - if ($k == 'CCANOMALOUS') { - $v = number_format($v, 1); - } - - $beam = array( - 'XBEAM', - 'YBEAM', - 'REFINEDXBEAM', - 'REFINEDYBEAM', - ); - - // We need to discriminate between NULL values from the database and when the value is a zero - if (in_array($k, $beam) && is_numeric($v)) { - $v = number_format($v, 2); - } + $processing_job_query = $this->_autoprocessing_query_builder( + "api.datacollectionid = :1 AND app.processingstatus IS NOT NULL", + "GROUP BY app.autoprocprogramid, apss.autoprocscalingstatisticsid", + "ORDER BY apss.scalingstatisticstype DESC"); + $rows = $this->db->pq($processing_job_query, array($id)); - if ( - $k == 'AUTOPROCPROGRAMID' || - $k == 'SHELL' || - $k == 'PROCESSINGJOBID' || - $k == 'IMAGESWEEPCOUNT' - ) { - continue; - } elseif ($k == 'SG') { - $output[$r['AUTOPROCPROGRAMID']]['SG'] = $v; - } elseif (in_array(strtolower($k), $dts2)) { - $shell[$k] = number_format($v, 2); - } elseif (in_array(strtolower($k), $dts)) { - $v = number_format($v, 2); - $output[$r['AUTOPROCPROGRAMID']]['CELL'][$k] = $v; - } elseif (in_array($k, $beam)) { - $output[$r['AUTOPROCPROGRAMID']]['BEAM'][$k] = $v; - } else { - $shell[$k] = $v; - } - } - $output[$r['AUTOPROCPROGRAMID']]['SHELLS'][ - $r['SHELL'] - ] = $shell; - } + $whereClause = "api.datacollectionid=:1"; + $messages = $this->_generate_auto_program_messages($whereClause, "", array($id)); - $output[$r['AUTOPROCPROGRAMID']]['PROCESSINGJOBID'] = - $r['PROCESSINGJOBID']; - $output[$r['AUTOPROCPROGRAMID']]['IMAGESWEEPCOUNT'] = - $r['IMAGESWEEPCOUNT']; - $output[$r['AUTOPROCPROGRAMID']]['DCCOUNT'] = $r['DCCOUNT']; - - $output[$r['AUTOPROCPROGRAMID']]['TYPE'] = $r['TYPE']; - $output[$r['AUTOPROCPROGRAMID']]['AID'] = $r['AUTOPROCPROGRAMID']; - $output[$r['AUTOPROCPROGRAMID']]['PROCESSINGSTATUS'] = - $r['PROCESSINGSTATUS']; - $output[$r['AUTOPROCPROGRAMID']]['PROCESSINGMESSAGE'] = - $r['PROCESSINGMESSAGE']; - $output[$r['AUTOPROCPROGRAMID']]['MESSAGES'] = array_key_exists( - $r['AUTOPROCPROGRAMID'], - $messages - ) - ? $messages[$r['AUTOPROCPROGRAMID']] - : array(); - $output[$r['AUTOPROCPROGRAMID']]['AUTOMATIC'] = - $r['AUTOMATIC'] != 0; - } + $output = $this->_format_auto_processing_result($rows, $messages); $this->_output(array(sizeof($output), $output)); } @@ -534,7 +403,7 @@ function _get_downstreams($dcid = null, $aid = null) { app.processingstatus, app.processingmessage, app.processingstarttime, app.processingendtime, pj.recipe, pj.comments as processingcomments, dc.imageprefix as dcimageprefix, dc.imagedirectory as dcimagedirectory, - CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, + CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, GROUP_CONCAT(CONCAT(pjp.parameterkey, '=', pjp.parametervalue)) as parameters FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid @@ -546,7 +415,7 @@ function _get_downstreams($dcid = null, $aid = null) { INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE api.autoprocintegrationid IS NULL AND p.proposalid=:1 $where AND app.processingprograms NOT IN ('$filter') - GROUP BY pj.processingjobid", + GROUP BY app.autoprocprogramid", $args ); @@ -555,8 +424,10 @@ function _get_downstreams($dcid = null, $aid = null) { if ($downstream["PARAMETERS"]) { $str_params = explode(',', $downstream["PARAMETERS"]); foreach ($str_params as $str_param) { - list($key, $value) = explode('=', $str_param); - $params[$key] = $value; + if (strpos($str_param, '=') !== false) { + list($key, $value) = explode('=', $str_param); + $params[$key] = $value; + } } } $downstream['PARAMETERS'] = $params; @@ -649,20 +520,27 @@ function _downstream_images($aid) { $im = $plugin->images($this->has_arg('n') ? $this->arg("n") : 0); if ($im) { if (file_exists($im)) { - $ext = pathinfo($im, PATHINFO_EXTENSION); - if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) { - $head = 'image/' . $ext; - } - + $this->_set_content_type_header($im); $this->_browser_cache(); - $this->app->contentType($head); readfile($im); + } elseif (file_exists($im.'.gz')) { + $this->_set_content_type_header($im); + $this->_browser_cache(); + readgzfile($im.'.gz'); } else { $this->_error("No such image"); } } } + function _set_content_type_header($im) { + $ext = pathinfo($im, PATHINFO_EXTENSION); + if (in_array($ext, array('png', 'jpg', 'jpeg', 'gif'))) { + $head = 'image/' . $ext; + $this->app->contentType($head); + } + } + function _browser_cache() { $expires = 60 * 60 * 24 * 14; $this->app->response->headers->set('Pragma', 'public'); @@ -720,8 +598,276 @@ function _downstream_mapmodel($aid) { filesize($mapmodel) ); readfile($mapmodel); + } elseif (file_exists($mapmodel.'.gz')) { + readgzfile($mapmodel.'.gz'); } else { $this->_error('Could not find map / model'); } } + + /** + * Processing results by sample groups + * + */ + function _get_latest_multiplex_job_result() { + if (!$this->has_arg('prop')) { + $this->_error('proposal specified'); + } + + if (!$this->has_arg('blSampleGroupId')) { + $this->_error('No sample group specified'); + } + + $args = array($this->arg('blSampleGroupId')); + + $result_count = $this->has_arg('resultCount') ? $this->arg('resultCount') : ''; + $result_count = empty($result_count) ? 3 : $this->arg('resultCount'); + array_push($args, $result_count); + + $last_processing_job_result = $this->db->pq(" + SELECT pj.processingJobId + FROM BLSampleGroup_has_BLSample bhg + JOIN DataCollectionGroup dcg ON dcg.blSampleId = bhg.blSampleId + JOIN DataCollection dc ON dc.dataCollectionGroupId = dcg.dataCollectionGroupId + JOIN ProcessingJob pj ON pj.dataCollectionId = dc.dataCollectionId + JOIN ProcessingJobParameter pjp ON pj.processingJobId = pjp.processingJobId + WHERE pjp.parameterKey = 'sample_group_id' AND pjp.parameterValue =:1 + ORDER BY processingJobId DESC + LIMIT :2", + $args); + + if (sizeof($last_processing_job_result) < 1) { + $this->_output(array()); + return; + } + + $processing_ids_list = array_column($last_processing_job_result, 'PROCESSINGJOBID'); + $processing_jobs = array_reduce(array_keys($processing_ids_list), function ($carry, $index) use ($processing_ids_list) { + $index_value = $index + 1; + + if ($index_value < sizeof($processing_ids_list)) { + $carry .= ":{$index_value}, "; + } else { + $carry .= ":{$index_value}"; + } + return $carry; + }, ""); + + $whereClause = "app.processingJobId IN ({$processing_jobs})"; + $job_where_clause = "pj.processingJobId IN ({$processing_jobs})"; + $message_join = "INNER JOIN autoprocprogram app on api.autoprocprogramid = app.autoprocprogramid"; + + $multiplex_job_query = $this->_autoprocessing_query_builder( + $job_where_clause, + "GROUP BY app.autoprocprogramid, apss.autoprocscalingstatisticsid", + "ORDER BY processingJobId DESC"); + $last_multiplex_jobs = $this->db->pq($multiplex_job_query, $processing_ids_list); + $multiplex_jobs_messages = $this->_generate_auto_program_messages($whereClause, $message_join, $processing_ids_list); + $output = $this->_format_auto_processing_result($last_multiplex_jobs, $multiplex_jobs_messages); + $this->_output($output); + } + + private function _autoprocessing_query_builder($where, $group, $order = '') { + $fields = " + apss.cchalf, + apss.ccanomalous, + apss.anomalous, + dc.xbeam, + dc.ybeam, + api.refinedxbeam, + api.refinedybeam, + app.autoprocprogramid, + app.processingprograms as type, + apss.ntotalobservations as ntobs, + apss.ntotaluniqueobservations as nuobs, + apss.resolutionlimitlow as rlow, + apss.resolutionlimithigh as rhigh, + apss.scalingstatisticstype as shell, + apss.rmeasalliplusiminus as rmeas, + apss.rmerge, apss.completeness, + apss.anomalouscompleteness as anomcompleteness, + apss.anomalousmultiplicity as anommultiplicity, + apss.multiplicity, + apss.meanioversigi as isigi, + apss.resioversigi2 as resisigi, + ap.spacegroup as sg, + ap.refinedcell_a as cell_a, + ap.refinedcell_b as cell_b, + ap.refinedcell_c as cell_c, + ap.refinedcell_alpha as cell_al, + ap.refinedcell_beta as cell_be, + ap.refinedcell_gamma as cell_ga, + (SELECT COUNT(api1.autoprocintegrationid) FROM autoprocintegration api1 WHERE api1.autoprocprogramid = app.autoprocprogramid) as imagesweepcount, + app.processingstatus, + app.processingmessage, + count(distinct pjis.datacollectionid) as dccount, + max(pjis.processingjobid) as processingjobid, + pj.automatic"; + + $from = "FROM autoprocintegration api"; + $joins = " + LEFT OUTER JOIN autoprocscaling_has_int aph ON api.autoprocintegrationid = aph.autoprocintegrationid + LEFT OUTER JOIN autoprocscaling aps ON aph.autoprocscalingid = aps.autoprocscalingid + LEFT OUTER JOIN autoproc ap ON aps.autoprocid = ap.autoprocid + LEFT OUTER JOIN autoprocscalingstatistics apss ON apss.autoprocscalingid = aph.autoprocscalingid + INNER JOIN autoprocprogram app ON api.autoprocprogramid = app.autoprocprogramid + LEFT OUTER JOIN processingjob pj ON app.processingjobid = pj.processingjobid + LEFT OUTER JOIN processingjobimagesweep pjis ON pjis.processingjobid = pj.processingjobid + INNER JOIN datacollection dc ON api.datacollectionid = dc.datacollectionid + "; + + return "SELECT $fields $from $joins WHERE $where $group $order"; + } + + private function _generate_auto_program_messages($where, $join = "", $args = array()) { + $messages = array(); + + $query = "SELECT api.autoprocprogramid, appm.recordtimestamp, appm.severity, appm.message, appm.description + FROM autoprocprogrammessage appm + INNER JOIN autoprocintegration api ON api.autoprocprogramid = appm.autoprocprogramid + $join + WHERE $where"; + + $messages_row = $this->db->pq($query, $args); + + foreach ($messages_row as $message) { + if (!array_key_exists($message['AUTOPROCPROGRAMID'], $messages)) { + $messages[$message['AUTOPROCPROGRAMID']] = array(); + } + array_push($messages[$message['AUTOPROCPROGRAMID']], $message); + } + + return $messages; + } + + private function _format_auto_processing_result($table_rows, $messages_result) { + $formatted_result = array(); + $cells_data = array( + 'cell_a', + 'cell_b', + 'cell_c', + 'cell_al', + 'cell_be', + 'cell_ga', + ); + $resolution_data = array('rlow', 'rhigh'); + $returned_keys = array('PROCESSINGJOBID', 'IMAGESWEEPCOUNT', 'DCCOUNT', 'TYPE', 'PROCESSINGSTATUS', 'PROCESSINGMESSAGE'); + + foreach($table_rows as &$row) { + if (!array_key_exists($row['AUTOPROCPROGRAMID'], $formatted_result)) { + $formatted_result[$row['AUTOPROCPROGRAMID']] = array_intersect_key($row, array_flip($returned_keys)); + $new_items = array( + 'BEAM' => array(), + 'SHELLS' => array(), + 'CELL' => array() + ); + $formatted_result[$row['AUTOPROCPROGRAMID']] = array_merge($formatted_result[$row['AUTOPROCPROGRAMID']], $new_items); + } + + if ($row['PROCESSINGSTATUS'] == '1') { + $shell = array(); + foreach ($row as $key => &$value) { + if ($key == 'TYPE') { + if ($row['DCCOUNT'] > 1) { + $prefix = preg_match('/multi/', $value) ? '' : 'multi-'; + $value = $row['DCCOUNT'] . 'x ' . $prefix . $value; + } + } + + + if ($key == 'ISIGI') { + $value = number_format($value, 1); + } + if ($key == 'RESISIGI') { + $value = number_format($value, 2); + } + if ($key == 'RMERGE') { + $value = number_format($value, 3); + } + if ($key == 'RMEAS') { + $value = number_format($value, 3); + } + if ($key == 'COMPLETENESS') { + $value = number_format($value, 1); + } + if ($key == 'MULTIPLICITY') { + $value = number_format($value, 1); + } + if ($key == 'ANOMCOMPLETENESS') { + $value = number_format($value, 1); + } + if ($key == 'ANOMMULTIPLICITY') { + $value = number_format($value, 1); + } + if ($key == 'CCHALF') { + $value = number_format($value, 1); + } + if ($key == 'CCANOMALOUS') { + $value = number_format($value, 1); + } + + $beam = array( + 'XBEAM', + 'YBEAM', + 'REFINEDXBEAM', + 'REFINEDYBEAM', + ); + + // We need to discriminate between NULL values from the database and when the value is a zero + if (in_array($key, $beam) && is_numeric($value)) { + $value = number_format($value, 2); + } + + if ( + $key == 'AUTOPROCPROGRAMID' || + $key == 'SHELL' || + $key == 'PROCESSINGJOBID' || + $key == 'IMAGESWEEPCOUNT' + ) { + continue; + } elseif ($key == 'SG') { + $formatted_result[$row['AUTOPROCPROGRAMID']]['SG'] = $value; + } elseif (in_array(strtolower($key), $resolution_data)) { + $shell[$key] = number_format($value, 2); + } elseif (in_array(strtolower($key), $cells_data)) { + $value = number_format($value, 2); + $formatted_result[$row['AUTOPROCPROGRAMID']]['CELL'][$key] = $value; + } elseif (in_array($key, $beam)) { + $formatted_result[$row['AUTOPROCPROGRAMID']]['BEAM'][$key] = $value; + } else { + $shell[$key] = $value; + } + } + + // Allow Shells to always be returned in order of 'OUTERSHELL', 'INNERSHELL', 'OVERALL' + switch($row['SHELL']) { + case 'OUTERSHELL': + case 'outerShell': + $shell['INDEX'] = 0; + break; + case 'INNERSHELL': + case 'innerShell': + $shell['INDEX'] = 1; + break; + case 'OVERALL': + case 'overall': + $shell['INDEX'] = 2; + break; + default: + $shell['INDEX'] = 3; + } + + $formatted_result[$row['AUTOPROCPROGRAMID']]['SHELLS'][$row['SHELL']] = $shell; + } + + $formatted_result[$row['AUTOPROCPROGRAMID']]['TYPE'] = $row['TYPE']; + $formatted_result[$row['AUTOPROCPROGRAMID']]['AID'] = $row['AUTOPROCPROGRAMID']; + $formatted_result[$row['AUTOPROCPROGRAMID']]['MESSAGES'] = array_key_exists($row['AUTOPROCPROGRAMID'], $messages_result) + ? $messages_result[$row['AUTOPROCPROGRAMID']] + : array(); + $formatted_result[$row['AUTOPROCPROGRAMID']]['AUTOMATIC'] = $row['AUTOMATIC'] != 0; + } + + return $formatted_result; + } } diff --git a/api/src/Page/Projects.php b/api/src/Page/Projects.php index 1bc8f88b5..34e4eb239 100644 --- a/api/src/Page/Projects.php +++ b/api/src/Page/Projects.php @@ -6,233 +6,276 @@ class Projects extends Page { - - - public static $arg_list = array('pid' => '\d+', - 'ty' => '\w+', - 'iid' => '\d+', - 'rem' => '\d', - - 'TITLE' => '.*', - 'ACRONYM' => '([\w-])+', - - 'PROJECTID' => '\d+', - 'USERNAME' => '\w+\d+', - 'PERSONID' => '\d+', - 'PPID' => '\d+-\d+', - ); - - - public static $dispatch = array(array('(/:pid)', 'get', '_projects'), - array('', 'post', '_add_project'), - array('/:pid', 'patch', '_update_project'), - - array('/users', 'post', '_add_user'), - array('/users/:PPID', 'delete', '_del_user'), - - array('/check/ty/:ty/pid/:pid/iid/:iid', 'get', '_check_project'), - array('/addto/pid/:pid/ty/:ty/iid/:iid(/rem/:rem)', 'get', '_add_to_project'), - - #array('/migrate', 'get', '_migrate'), - ); - - - var $types = array('protein' => array('project_has_protein', 'proteinid'), - 'sample' => array('project_has_blsample', 'blsampleid'), - 'edge' => array('project_has_energyscan', 'energyscanid'), - 'mca' => array('project_has_xfefspectrum', 'xfefluorescencespectrumid'), - 'dc' => array('project_has_dcgroup', 'datacollectiongroupid'), - ); - - - # List of projects - function _projects() { - $args = array($this->user->personid, $this->user->personid); - $where = "WHERE (p.personid LIKE :1 OR php.personid LIKE :2)"; - - $tot = $this->db->pq("SELECT count(distinct p.projectid) as tot + + + public static $arg_list = array('pid' => '\d+', + 'ty' => '\w+', + 'iid' => '\d+', + 'rem' => '\d', + + 'TITLE' => '.*', + 'ACRONYM' => '([\w\-])+', + + 'PROJECTID' => '\d+', + 'USERNAME' => '\w+\d+', + 'PERSONID' => '\d+', + 'PPID' => '\d+-\d+', + ); + + + public static $dispatch = array(array('(/:pid)', 'get', '_projects'), + array('', 'post', '_add_project'), + array('/:pid', 'patch', '_update_project'), + + array('/users', 'post', '_add_user'), + array('/users/:PPID', 'delete', '_del_user'), + + array('/check/ty/:ty/pid/:pid/iid/:iid', 'get', '_check_project'), + array('/addto/pid/:pid/ty/:ty/iid/:iid(/rem/:rem)', 'get', '_add_to_project'), + + #array('/migrate', 'get', '_migrate'), + ); + + + var $types = array('protein' => array('project_has_protein', 'proteinid'), + 'sample' => array('project_has_blsample', 'blsampleid'), + 'edge' => array('project_has_energyscan', 'energyscanid'), + 'mca' => array('project_has_xfefspectrum', 'xfefluorescencespectrumid'), + 'dc' => array('project_has_dcgroup', 'datacollectiongroupid'), + ); + + + # List of projects + function _projects() + { + $args = array($this->user->personId, $this->user->personId); + $where = "WHERE (p.personid LIKE :1 OR php.personid LIKE :2)"; + + $tot = $this->db->pq("SELECT count(distinct p.projectid) as tot FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid $where", $args); - $tot = intval($tot[0]['TOT']); - - if ($this->has_arg('pid')) { - $where .= ' AND p.projectid=:'.(sizeof($args)+1); - array_push($args, $this->arg('pid')); - } - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT p.title, p.projectid, p.acronym, p.personid, CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as person + $tot = intval($tot[0]['TOT']); + + if ($this->has_arg('pid')) + { + $where .= ' AND p.projectid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('pid')); + } + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) + { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT p.title, p.projectid, p.acronym, p.personid, CONCAT(pe.givenname, ' ', pe.familyname) as person FROM project p INNER JOIN person pe ON p.personid = pe.personid LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid $where ORDER BY p.projectid", $args); - - foreach ($rows as &$ro) { - $ro['IS_OWNER'] = $ro['PERSONID'] == $this->user->personid; - } - - if ($this->has_arg('pid')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such project'); - - } else { - $this->_output(array('total' => $tot, - 'data' => $rows, - )); - } + + foreach ($rows as &$ro) + { + $ro['IS_OWNER'] = $ro['PERSONID'] == $this->user->personId; } - - - function _add_project() { - if (!$this->has_arg('TITLE')) $this->_error('No title specified'); - if (!$this->has_arg('ACRONYM')) $this->_error('No acronym specified'); - - $this->db->pq("INSERT INTO project (projectid,title,acronym,personid) - VALUES (s_project.nextval, :1, :2, :3) RETURNING projectid INTO :id", - array($this->arg('TITLE'), $this->arg('ACRONYM'), $this->user->personid)); - - $this->_output(array('PROJECTID' => $this->db->id(), - 'IS_OWNER' => True, - 'PERSON' => $this->user->givenname.' '.$this->user->familyname + + if ($this->has_arg('pid')) + { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such project'); + + } + else + { + $this->_output(array('total' => $tot, + 'data' => $rows, )); } - - - # Add to / remove from project - function _add_to_project() { - if (!$this->has_arg('pid')) $this->_error('No project id specified'); - if (!$this->has_arg('ty')) $this->_error('No item type specified'); - if (!$this->has_arg('iid')) $this->_error('No item id specified'); - - - if (array_key_exists($this->arg('ty'), $this->types)) { - $t = $this->types[$this->arg('ty')]; - - $chk = $this->db->pq("SELECT projectid FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); - - if ($this->has_arg('rem') && sizeof($chk)) { - $this->db->pq("DELETE FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); - } - - if (!sizeof($chk)) { - $this->db->pq("INSERT INTO $t[0] (projectid,$t[1]) VALUES (:1, :2)", array($this->arg('pid'), $this->arg('iid'))); - } - - $this->_output(1); + } + + + function _add_project() + { + if (!$this->has_arg('TITLE')) + $this->_error('No title specified'); + if (!$this->has_arg('ACRONYM')) + $this->_error('No acronym specified'); + + $this->db->pq("INSERT INTO project (projectid,title,acronym,personid) + VALUES (s_project.nextval, :1, :2, :3) RETURNING projectid INTO :id", + array($this->arg('TITLE'), $this->arg('ACRONYM'), $this->user->personId)); + + $this->_output(array('PROJECTID' => $this->db->id(), + 'IS_OWNER' => True, + 'PERSON' => $this->user->givenName . ' ' . $this->user->familyName + )); + } + + + # Add to / remove from project + function _add_to_project() + { + if (!$this->has_arg('pid')) + $this->_error('No project id specified'); + if (!$this->has_arg('ty')) + $this->_error('No item type specified'); + if (!$this->has_arg('iid')) + $this->_error('No item id specified'); + + + if (array_key_exists($this->arg('ty'), $this->types)) + { + $t = $this->types[$this->arg('ty')]; + + $chk = $this->db->pq("SELECT projectid FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); + + if ($this->has_arg('rem') && sizeof($chk)) + { + $this->db->pq("DELETE FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); } - } - - - # Check if item already exists - function _check_project() { - if (!$this->has_arg('pid')) $this->_error('No project id specified'); - if (!$this->has_arg('ty')) $this->_error('No item type specified'); - if (!$this->has_arg('iid')) $this->_error('No item id specified'); - - - $ret = 0; - - if (array_key_exists($this->arg('ty'), $this->types)) { - $t = $this->types[$this->arg('ty')]; - - $rows = $this->db->pq("SELECT projectid FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); - - if (sizeof($rows)) $ret = 1; + + if (!sizeof($chk)) + { + $this->db->pq("INSERT INTO $t[0] (projectid,$t[1]) VALUES (:1, :2)", array($this->arg('pid'), $this->arg('iid'))); } - - $this->_output($ret); + + $this->_output(1); } - - - # Update project - function _update_project() { - if (!$this->has_arg('pid')) $this->_error('No project id specified'); - - $proj = $this->db->pq("SELECT p.projectid FROM project p WHERE p.personid LIKE :1 AND p.projectid=:2", array($this->user->personid,$this->arg('pid'))); - if (!sizeof($proj)) $this->_error('No such project'); - - $fields = array('ACRONYM', 'TITLE'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE project SET $f=:1 WHERE projectid=:2", array($this->arg($f), $this->arg('pid'))); - $this->_output(array($f => $this->arg($f))); - } - } + } + + + # Check if item already exists + function _check_project() + { + if (!$this->has_arg('pid')) + $this->_error('No project id specified'); + if (!$this->has_arg('ty')) + $this->_error('No item type specified'); + if (!$this->has_arg('iid')) + $this->_error('No item id specified'); + + + $ret = 0; + + if (array_key_exists($this->arg('ty'), $this->types)) + { + $t = $this->types[$this->arg('ty')]; + + $rows = $this->db->pq("SELECT projectid FROM $t[0] WHERE projectid=:1 AND $t[1]=:2", array($this->arg('pid'), $this->arg('iid'))); + + if (sizeof($rows)) + $ret = 1; } + $this->_output($ret); + } - # Add a user to a project - function _add_user() { - if (!$this->has_arg('PROJECTID')) $this->_error('No project id specified'); - if (!$this->has_arg('PERSONID')) $this->_error('No user specified'); - - $proj = $this->db->pq("SELECT p.projectid FROM project p WHERE p.personid LIKE :1 AND p.projectid=:2", array($this->user->personid,$this->arg('PROJECTID'))); - - if (!sizeof($proj)) $this->_error('No such project'); - $proj = $proj[0]; - - $person = $this->db->pq("SELECT CONCAT(CONCAT(givenname, ' '), familyname) as fullname FROM person WHERE personid=:1", array($this->arg('PERSONID'))); - if (!sizeof($person)) $this->_error('No such person'); - $person = $person[0]; - $this->db->pq("INSERT INTO project_has_person (projectid, personid) VALUES (:1, :2)", array($this->arg('PROJECTID'), $this->arg('PERSONID'))); + # Update project + function _update_project() + { + if (!$this->has_arg('pid')) + $this->_error('No project id specified'); - $this->_output(array('FULLNAME' => $person['FULLNAME'])); + $proj = $this->db->pq("SELECT p.projectid FROM project p WHERE p.personid LIKE :1 AND p.projectid=:2", array($this->user->personId, $this->arg('pid'))); + if (!sizeof($proj)) + $this->_error('No such project'); + + $fields = array('ACRONYM', 'TITLE'); + foreach ($fields as $f) + { + if ($this->has_arg($f)) + { + $this->db->pq("UPDATE project SET $f=:1 WHERE projectid=:2", array($this->arg($f), $this->arg('pid'))); + $this->_output(array($f => $this->arg($f))); + } } - - - # Remove a user - function _del_user() { - if (!$this->has_arg('PPID')) $this->_error('No project id specified'); - list($pid, $pjid) = explode('-', $this->arg('PPID')); - - $proj = $this->db->pq("SELECT p.projectid + } + + + # Add a user to a project + function _add_user() + { + if (!$this->has_arg('PROJECTID')) + $this->_error('No project id specified'); + if (!$this->has_arg('PERSONID')) + $this->_error('No user specified'); + + $proj = $this->db->pq("SELECT p.projectid FROM project p WHERE p.personid LIKE :1 AND p.projectid=:2", array($this->user->personId, $this->arg('PROJECTID'))); + + if (!sizeof($proj)) + $this->_error('No such project'); + $proj = $proj[0]; + + $person = $this->db->pq("SELECT CONCAT(givenname, ' ', familyname) as fullname FROM person WHERE personid=:1", array($this->arg('PERSONID'))); + if (!sizeof($person)) + $this->_error('No such person'); + $person = $person[0]; + + $this->db->pq("INSERT INTO project_has_person (projectid, personid) VALUES (:1, :2)", array($this->arg('PROJECTID'), $this->arg('PERSONID'))); + + $this->_output(array('FULLNAME' => $person['FULLNAME'])); + } + + + # Remove a user + function _del_user() + { + if (!$this->has_arg('PPID')) + $this->_error('No project id specified'); + list($pid, $pjid) = explode('-', $this->arg('PPID')); + + $proj = $this->db->pq("SELECT p.projectid FROM project p INNER JOIN project_has_person php ON php.projectid = p.projectid - WHERE p.personid LIKE :1 AND php.personid=:2 AND p.projectid=:3", array($this->user->personid, $pid, $pjid)); - if (!sizeof($proj)) $this->_error('No such project'); - $proj = $proj[0]; - - $this->db->pq("DELETE FROM project_has_person WHERE personid=:1 AND projectid=:2", array($pid, $pjid)); - - $this->_output(1); - } + WHERE p.personid LIKE :1 AND php.personid=:2 AND p.projectid=:3", array($this->user->personId, $pid, $pjid)); + if (!sizeof($proj)) + $this->_error('No such project'); + $proj = $proj[0]; + + $this->db->pq("DELETE FROM project_has_person WHERE personid=:1 AND projectid=:2", array($pid, $pjid)); + $this->_output(1); + } - function _migrate() { - $projects = $this->db->pq("SELECT owner,projectid FROM project"); - foreach($projects as $p) { - $person = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($p['OWNER'])); - if (sizeof($person)) { - $person = $person[0]; - $this->db->pq("UPDATE project SET personid=:1 WHERE projectid=:2", array($person['PERSONID'], $p['PROJECTID'])); - print_r(array('Migrated Project', $p['PROJECTID'], 'owner', $p['OWNER'], 'to person', $person['PERSONID'])); - } + + function _migrate() + { + $projects = $this->db->pq("SELECT owner,projectid FROM project"); + foreach ($projects as $p) + { + $person = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($p['OWNER'])); + if (sizeof($person)) + { + $person = $person[0]; + $this->db->pq("UPDATE project SET personid=:1 WHERE projectid=:2", array($person['PERSONID'], $p['PROJECTID'])); + print_r(array('Migrated Project', $p['PROJECTID'], 'owner', $p['OWNER'], 'to person', $person['PERSONID'])); } + } - $pu = $this->db->pq("SELECT projectid, username FROM project_has_user"); - foreach ($pu as $u) { - $person = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($u['USERNAME'])); - if (sizeof($person)) { - $person = $person[0]; - $this->db->pq('INSERT INTO project_has_person (projectid, personid) VALUES (:1, :2)', array($u['PROJECTID'], $person['PERSONID'])); - print_r(array('Migrated Project_has_user', $u['PROJECTID'], 'username', $u['USERNAME'], 'to person', $person['PERSONID'])); - } + $pu = $this->db->pq("SELECT projectid, username FROM project_has_user"); + foreach ($pu as $u) + { + $person = $this->db->pq("SELECT personid FROM person WHERE login=:1", array($u['USERNAME'])); + if (sizeof($person)) + { + $person = $person[0]; + $this->db->pq('INSERT INTO project_has_person (projectid, personid) VALUES (:1, :2)', array($u['PROJECTID'], $person['PERSONID'])); + print_r(array('Migrated Project_has_user', $u['PROJECTID'], 'username', $u['USERNAME'], 'to person', $person['PERSONID'])); } } + } } diff --git a/api/src/Page/Proposal.php b/api/src/Page/Proposal.php index 053bc53bf..dc9acc00a 100644 --- a/api/src/Page/Proposal.php +++ b/api/src/Page/Proposal.php @@ -9,402 +9,434 @@ class Proposal extends Page { - public static $arg_list = array( - 'prop' => '\w+\d+', - 'value' => '.*', - 'visit' => '\w+\d+-\d+', - 'all' => '\d', - 'year' => '\d\d\d\d', - 'month' => '\d+', - 'bl' => '[\w-]+', - 'ty' => '\w+', - 'cm' => '\d', - 'ty' => '\w+', - 'next' => '\d', - 'prev' => '\d', - 'started' => '\d', - 'scheduled' => '\d', - 'current' => '\d', - - 'COMMENTS' => '(\w|\s|-)+', - - - 'BLSAMPLEID' => '\d+', - 'CRYSTALID' => '\d+', - 'PROTEINID' => '\d+', - 'CONTAINERID' => '\d+', - 'DEWARID' => '\d+', - 'SHIPPINGID' => '\d+', - 'LABCONTACTID' => '\d+', - 'DATACOLLECTIONID' => '\d+', - - // proposal - 'PROPOSALCODE' => '\w+', - 'PROPOSALNUMBER' => '\d+', - 'TITLE' => '(\w|\s|\-|\(|\))+', - 'PERSONID' => '\d+', - 'STATE' => '\w+', - 'EXTERNALID' => '\w+', - - // visit - 'PROPOSALID' => '\d+', - 'STARTDATE' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'ENDDATE' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', - 'BEAMLINENAME' => '[\w-]+', - 'BEAMLINEOPERATOR' => '(\w|\s|-)+', - 'SCHEDULED' => '\d', - 'ARCHIVED' => '\d', - 'SESSIONTYPE' => '\w+', - 'BEAMLINESETUPID' => '\d+', - 'BEAMCALENDARID' => '\d+', - 'VISITNUMBER' => '\d+', - - // visit has person - 'SHPKEY' => '\d+\-\d+', - 'SESSIONID' => '\d+', - 'ROLE' => '([\w\s-])+', - 'REMOTE' => '\d', - - // Updating Used Time when a session is closed - 'Total Time' => '.*' - ); - - public static $dispatch = array(array('(/:prop)', 'get', '_get_proposals'), - array('/', 'post', '_add_proposal'), - array('/:prop', 'patch', '_update_proposal'), - - array('/visits(/:visit)', 'get', '_get_visits'), - array('/visits/:visit', 'patch', '_update_visit'), - array('/visits', 'post', '_add_visit'), - - array('/visits/users', 'get', '_get_visit_users'), - array('/visits/users', 'post', '_add_visit_user'), - array('/visits/users/:SHPKEY', 'patch', '_update_visit_user'), - array('/visits/users/:SHPKEY', 'delete', '_remove_visit_user'), - - array('/calendar', 'get', '_get_beam_calendar'), - - array('/bls/:ty', 'get', '_get_beamlines'), - array('/type', 'get', '_get_types'), - array('/lookup', 'get', '_lookup'), - array('/bl/:prop', 'get', '_get_beamline_from_proposal'), - - array('/auto', 'get', '_auto_visit'), - array('/auto', 'delete', '_close_auto_visit'), - array('/auto', 'patch', '_update_auto_visit'), - ); - - - function _get_beamline_from_proposal(){ - if(!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $bl = $this->db->pq("SELECT beamLineName + public static $arg_list = array( + 'prop' => '\w+\d+', + 'value' => '.*', + 'visit' => '\w+\d+-\d+', + 'all' => '\d', + 'year' => '\d\d\d\d', + 'month' => '\d+', + 'bl' => '[\w\-]+', + 'ty' => '\w+', + 'cm' => '\d', + 'next' => '\d', + 'prev' => '\d', + 'started' => '\d', + 'scheduled' => '\d', + 'current' => '\d', + 'notnull' => '\d+', + + 'COMMENTS' => '(\w|\s|-)+', + + + 'BLSAMPLEID' => '\d+', + 'CRYSTALID' => '\d+', + 'PROTEINID' => '\d+', + 'CONTAINERID' => '\d+', + 'DEWARID' => '\d+', + 'SHIPPINGID' => '\d+', + 'LABCONTACTID' => '\d+', + 'DATACOLLECTIONID' => '\d+', + + // proposal + 'PROPOSALCODE' => '\w+', + 'PROPOSALNUMBER' => '\d+', + 'TITLE' => '(\w|\s|\-|\(|\))+', + 'PERSONID' => '\d+', + 'STATE' => '\w+', + 'EXTERNALID' => '\w+', + + // visit + 'PROPOSALID' => '\d+', + 'STARTDATE' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'ENDDATE' => '\d\d-\d\d-\d\d\d\d \d\d:\d\d', + 'BEAMLINENAME' => '[\w\-]+', + 'BEAMLINEOPERATOR' => '(\w|\s|-)+', + 'SCHEDULED' => '\d', + 'ARCHIVED' => '\d', + 'SESSIONTYPE' => '\w+', + 'BEAMLINESETUPID' => '\d+', + 'BEAMCALENDARID' => '\d+', + 'VISITNUMBER' => '\d+', + + // visit has person + 'SHPKEY' => '\d+\-\d+', + 'SESSIONID' => '\d+', + 'ROLE' => '([\w\s\-])+', + 'REMOTE' => '\d', + + // Updating Used Time when a session is closed + 'Total Time' => '.*' + ); + + public static $dispatch = array( + array('(/:prop)', 'get', '_get_proposals'), + array('/', 'post', '_add_proposal'), + array('/:prop', 'patch', '_update_proposal'), + + array('/visits(/:visit)', 'get', '_get_visits'), + array('/visits/:visit', 'patch', '_update_visit'), + array('/visitscomm/:visit', 'patch', '_update_visit_comment'), + array('/visits', 'post', '_add_visit'), + + array('/visits/users', 'get', '_get_visit_users'), + array('/visits/users', 'post', '_add_visit_user'), + array('/visits/users/:SHPKEY', 'patch', '_update_visit_user'), + array('/visits/users/:SHPKEY', 'delete', '_remove_visit_user'), + + array('/calendar', 'get', '_get_beam_calendar'), + + array('/bls/:ty', 'get', '_get_beamlines'), + array('/type', 'get', '_get_types'), + array('/lookup', 'get', '_lookup'), + array('/bl/:prop', 'get', '_get_beamline_from_proposal'), + + array('/auto', 'get', '_auto_visit'), + array('/auto', 'delete', '_close_auto_visit'), + array('/auto', 'patch', '_update_auto_visit'), + ); + + + function _get_beamline_from_proposal() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + $bl = $this->db->pq("SELECT beamLineName FROM BLSession bls INNER JOIN Proposal p ON bls.proposalId = p.proposalId WHERE CONCAT(p.proposalCode, p.proposalNumber) = :1", array($this->arg('prop')))[0]; - if(empty($bl)) $this->_error('Beamline not found!'); + if (empty($bl)) + $this->_error('Beamline not found!'); - $this->_output($bl); - } - + $this->_output($bl); + } - function _get_beamlines() { - if (!$this->has_arg('ty')) $this->_error('No type specified'); + function _get_beamlines() + { - $bls = $this->_get_beamlines_from_type($this->arg('ty')); + if (!$this->has_arg('ty')) + $this->_error('No type specified'); - if (empty($bls)) $this->_error('No such proposal type'); + $bls = $this->_get_beamlines_from_type($this->arg('ty')); - $this->_output($bls); - } + if (empty($bls)) + $this->_error('No such proposal type'); + $this->_output($bls); + } - function _get_types() { - $bls = implode("', '", $this->_get_beamlines_from_type($this->ty)); - $rows = $this->db->pq("SELECT distinct p.proposalcode + + function _get_types() + { + $bls = implode("', '", $this->_get_beamlines_from_type($this->ty)); + $rows = $this->db->pq("SELECT distinct p.proposalcode FROM proposal p INNER JOIN blsession s ON s.proposalid = p.proposalid WHERE s.beamlinename IN ('$bls')"); - $this->_output($rows); - } + $this->_output($rows); + } - # ------------------------------------------------------------------------ - # List proposals for current user - function _get_proposals($id=null) { - global $prop_types; + # ------------------------------------------------------------------------ + # List proposals for current user + function _get_proposals($id = null) + { + global $prop_types; - $args = array(); - $where = "WHERE 1=1"; + $args = array(); + $where = "WHERE 1=1"; - if ($id) { - $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :".(sizeof($args)+1); - array_push($args, $id); - } + if ($id) { + $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :" . (sizeof($args) + 1); + array_push($args, $id); + } - if ($this->staff) { - if (!$this->user->has('super_admin')) { - $bls = array(); - foreach ($this->user->perms as $p) { - if (strpos($p, '_admin')) { - $parts = explode('_', $p); - $ty = $parts[0]; - // _get_beamlines_from_type returns an empty array if type not found, so we can just merge.... - $bls = array_merge($bls, $this->_get_beamlines_from_type($ty)); - } + if ($this->staff) { + if (!$this->user->hasPermission('super_admin')) { + $bls = array(); + foreach ($this->user->perms as $p) { + if (strpos($p, '_admin')) { + $parts = explode('_', $p); + $ty = $parts[0]; + // _get_beamlines_from_type returns an empty array if type not found, so we can just merge.... + $bls = array_merge($bls, $this->_get_beamlines_from_type($ty)); } - - $where .= " AND s.beamlinename in ('".implode("','", $bls)."')"; } - } else { - $where = " INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid ".$where; - $where .= " AND shp.personid=:".(sizeof($args)+1); - array_push($args, $this->user->personid); - } - - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(s.beamlinename) LIKE lower(:".$st.") OR lower(p.title) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."),'%')) OR lower(CONCAT(p.proposalcode, p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')))"; - for ($i = 0; $i < 3; $i++) array_push($args, $this->arg('s')); + + $where = " LEFT OUTER JOIN session_has_person shp ON shp.sessionid = s.sessionid " . $where; + $where .= " AND (shp.personid=:" . (sizeof($args) + 1) . " OR s.beamlinename in ('" . implode("','", $bls) . "'))"; + array_push($args, $this->user->personId); } + } else { + $where = " INNER JOIN session_has_person shp ON shp.sessionid = s.sessionid " . $where; + $where .= " AND shp.personid=:" . (sizeof($args) + 1); + array_push($args, $this->user->personId); + } - $tot = $this->db->pq("SELECT count(distinct p.proposalid) as tot FROM proposal p + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(s.beamlinename) LIKE lower(:" . $st . ") OR lower(p.title) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "),'%')) OR lower(CONCAT(p.proposalcode, p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')))"; + for ($i = 0; $i < 3; $i++) + array_push($args, $this->arg('s')); + } + + $tot = $this->db->pq("SELECT count(distinct p.proposalid) as tot FROM proposal p LEFT OUTER JOIN blsession s ON p.proposalid = s.proposalid $where", $args); - $tot = intval($tot[0]['TOT']); - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - array_push($args, $start); - array_push($args, $end); - - $order = 'p.proposalid DESC'; - - if ($this->has_arg('sort_by')) { - $cols = array('ST' => 'p.bltimestamp', 'PROPOSALCODE' => 'p.proposalcode', 'PROPOSALNUMBER' => 'p.proposalnumber', 'VCOUNT' => 'vcount', 'TITLE' => 'lower(p.title)'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $rows = $this->db->paginate("SELECT CONCAT(p.proposalcode,p.proposalnumber) as proposal, p.title, TO_CHAR(p.bltimestamp, 'DD-MM-YYYY') as st, p.proposalcode, p.proposalnumber, count(s.sessionid) as vcount, p.proposalid, CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as fullname, pe.personid, p.state, IF(p.state = 'Open', 1, 0) as active, CONCAT(p.proposalcode,p.proposalnumber) as prop + $tot = intval($tot[0]['TOT']); + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'p.proposalid DESC'; + + if ($this->has_arg('sort_by')) { + $cols = array('ST' => 'p.bltimestamp', 'PROPOSALCODE' => 'p.proposalcode', 'PROPOSALNUMBER' => 'p.proposalnumber', 'VCOUNT' => 'vcount', 'TITLE' => 'lower(p.title)'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + + $rows = $this->db->paginate("SELECT CONCAT(p.proposalcode,p.proposalnumber) as proposal, p.title, TO_CHAR(p.bltimestamp, 'DD-MM-YYYY') as st, p.proposalcode, p.proposalnumber, count(distinct s.sessionid) as vcount, p.proposalid, CONCAT(pe.givenname, ' ', pe.familyname) as fullname, pe.personid, p.state, IF(p.state = 'Open', 1, 0) as active, CONCAT(p.proposalcode,p.proposalnumber) as prop, s.beamlinename FROM proposal p LEFT OUTER JOIN blsession s ON p.proposalid = s.proposalid LEFT OUTER JOIN person pe ON pe.personid = p.personid - $where + $where GROUP BY TO_CHAR(p.bltimestamp, 'DD-MM-YYYY'), p.bltimestamp, p.proposalcode, p.proposalnumber, p.title, p.proposalid ORDER BY $order", $args); - - - foreach ($rows as &$r) { - // See if proposal code matches list in config - $found = False; - $ty = null; - foreach ($prop_types as $pty) { - if ($r['PROPOSALCODE'] == $pty) { - $ty = $pty; - $found = True; - } + + foreach ($rows as &$r) { + // See if proposal code matches list in config + $found = False; + $ty = null; + foreach ($prop_types as $pty) { + if ($r['PROPOSALCODE'] == $pty) { + $ty = $pty; + $found = True; } - - // Proposal code didnt match, work out what beamline the visits are on - if (!$found) { - $bls = $this->db->pq("SELECT s.beamlinename FROM blsession s WHERE s.proposalid=:1", array($r['PROPOSALID'])); - - if (sizeof($bls)) { - foreach ($bls as $bl) { - $b = $bl['BEAMLINENAME']; - $ty = $this->_get_type_from_beamline($b); - if ($ty) break; - } + } + + // Proposal code didnt match, work out what beamline the visits are on + if (!$found) { + $bls = $this->db->pq("SELECT s.beamlinename FROM blsession s WHERE s.proposalid=:1", array($r['PROPOSALID'])); + + if (sizeof($bls)) { + foreach ($bls as $bl) { + $b = $bl['BEAMLINENAME']; + $ty = $this->_get_type_from_beamline($b); + if ($ty) + break; } } - - if (!$ty) $ty = 'gen'; - $r['TYPE'] = $ty; - } - - if ($id) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such proposal'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); + + if (!$ty) + $ty = 'gen'; + $r['TYPE'] = $ty; } - + if ($id) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such proposal'); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } - # ------------------------------------------------------------------------ - # Add a proposal - function _add_proposal() { - if (!$this->user->can('manage_proposal')) $this->_error('No access'); - if (!$this->has_arg('PROPOSALCODE')) $this->_error('No proposal code specified'); - if (!$this->has_arg('PROPOSALNUMBER')) $this->_error('No proposalnumber specified'); - if (!$this->has_arg('TITLE')) $this->_error('No title specified'); - if (!$this->has_arg('PERSONID')) $this->_error('No PI specified'); - - $this->db->pq("INSERT INTO proposal (personid, proposalcode, proposalnumber, title, state) - VALUES (:1, :2, :3, :4, 'Open')", - array($this->arg('PERSONID'), $this->arg('PROPOSALCODE'), $this->arg('PROPOSALNUMBER'), $this->arg('TITLE'))); - $this->_output(array( - 'PROPOSALID' => $this->db->id(), - 'PROPOSAL' => $this->arg('PROPOSALCODE').$this->arg('PROPOSALNUMBER') - )); - } + # ------------------------------------------------------------------------ + # Add a proposal + function _add_proposal() + { + $this->haltIfLackingPermission('manage_proposal'); + + if (!$this->has_arg('PROPOSALCODE')) + $this->_error('No proposal code specified'); + if (!$this->has_arg('PROPOSALNUMBER')) + $this->_error('No proposalnumber specified'); + if (!$this->has_arg('TITLE')) + $this->_error('No title specified'); + if (!$this->has_arg('PERSONID')) + $this->_error('No PI specified'); + + $this->db->pq( + "INSERT INTO proposal (personid, proposalcode, proposalnumber, title, state) + VALUES (:1, :2, :3, :4, 'Open')", + array($this->arg('PERSONID'), $this->arg('PROPOSALCODE'), $this->arg('PROPOSALNUMBER'), $this->arg('TITLE')) + ); + $this->_output(array( + 'PROPOSALID' => $this->db->id(), + 'PROPOSAL' => $this->arg('PROPOSALCODE') . $this->arg('PROPOSALNUMBER') + )); + } - # ------------------------------------------------------------------------ - # Update a proposal - function _update_proposal() { - if (!$this->user->can('manage_proposal')) $this->_error('No access'); - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - $prop = $this->db->pq("SELECT p.proposalid FROM proposal p + # ------------------------------------------------------------------------ + # Update a proposal + function _update_proposal() + { + $this->haltIfLackingPermission('manage_proposal'); + + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + $prop = $this->db->pq("SELECT p.proposalid FROM proposal p WHERE CONCAT(p.proposalcode, p.proposalnumber) LIKE :1", array($this->arg('prop'))); - - if (!sizeof($prop)) $this->_error('No such proposal'); - - foreach (array('PROPOSALCODE', 'PROPOSALNUMBER', 'TITLE', 'PERSONID', 'STATE', 'EXTERNALID') as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE proposal SET $f=:1 WHERE proposalid=:2", array($this->arg($f), $prop[0]['PROPOSALID'])); - $this->_output(array($f => $this->arg($f))); - } + + if (!sizeof($prop)) + $this->_error('No such proposal'); + + foreach (array('PROPOSALCODE', 'PROPOSALNUMBER', 'TITLE', 'PERSONID', 'STATE', 'EXTERNALID') as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE proposal SET $f=:1 WHERE proposalid=:2", array($this->arg($f), $prop[0]['PROPOSALID'])); + $this->_output(array($f => $this->arg($f))); } } - + } - # ------------------------------------------------------------------------ - # Get visits for a proposal - function _get_visits($visit=null, $output=true) { - global $commissioning_code; - if ($this->has_arg('current')) { - $this->_current_visits(); - return; - } - - // if (!$this->staff && !$this->has_arg('prop')) $this->_error('No proposal specified'); - - if ($this->has_arg('all')) { - $args = array(); - $where = 'WHERE 1=1'; - // 'All' is used for the main summary view (Next, Last, Commissioning) - // Ignore session zero for this summary view - they should be included if a proposal is selected - $where .= " AND s.visit_number > 0"; - } else { - $props = $this->db->pq('SELECT proposalid as id FROM proposal WHERE CONCAT(proposalcode, proposalnumber) LIKE :1', array($this->arg('prop'))); - if (!sizeof($props)) $this->_error('No such proposal'); - else $p = $props[0]['ID']; + # ------------------------------------------------------------------------ + # Get visits for a proposal + function _get_visits($visit = null, $output = true) + { + global $commissioning_code; - $args = array($p); - $where = 'WHERE s.proposalid = :1'; - } - - if ($this->has_arg('year')) { - $where .= " AND TO_CHAR(s.startdate, 'YYYY') = :".(sizeof($args)+1); - array_push($args, $this->arg('year')); - } - - if ($this->has_arg('month')) { - $where .= " AND TO_CHAR(s.startdate, 'MM') = :".(sizeof($args)+1); - array_push($args, $this->arg('month')); - } - - if ($this->has_arg('prev')) { - $where .= " AND s.enddate < SYSDATE"; - } + if ($this->has_arg('current')) { + $this->_current_visits(); + return; + } - if ($this->has_arg('started')) { - $where .= " AND s.startdate < SYSDATE"; - } - - if ($this->has_arg('next')) { - $where .= " AND s.enddate > SYSDATE AND TO_CHAR(s.startdate,'YYYY') > 2009"; - $this->args['order'] = 'asc'; - $this->args['sort_by'] = 'ST'; - } - - if ($this->has_arg('bl')) { - $where .= " AND s.beamlinename = :".(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } + if ($this->has_arg('all')) { + $args = array(); + $where = 'WHERE 1=1'; + // 'All' is used for the main summary view (Next, Last, Commissioning) + // Ignore session zero for this summary view - they should be included if a proposal is selected + $where .= " AND s.visit_number > 0"; + } else { + $props = $this->db->pq('SELECT proposalid as id FROM proposal WHERE CONCAT(proposalcode, proposalnumber) LIKE :1', array($this->arg('prop'))); + if (!sizeof($props)) + $this->_error('No such proposal'); + else + $p = $props[0]['ID']; - if ($this->has_arg('cm')) { - $where .= " AND p.proposalcode LIKE :".(sizeof($args)+1)." AND s.startdate <= SYSDATE"; - array_push($args, $commissioning_code); - } - - if ($this->has_arg('ty')) { - $beamlines = $this->_get_beamlines_from_type($this->arg('ty')); + $args = array($p); + $where = 'WHERE s.proposalid = :1'; + } - if (!empty($beamlines)) { - $bls = implode("', '", $beamlines); - $where .= " AND s.beamlinename IN ('$bls')"; - } - } + if ($this->has_arg('notnull')) { + $where .= ' AND s.visit_number is not NULL'; + } - if ($this->has_arg('s')) { - $where .= " AND s.visit_number LIKE :".(sizeof($args)+1); - array_push($args, $this->arg('s')); - } + if ($this->has_arg('year')) { + $where .= " AND TO_CHAR(s.startdate, 'YYYY') = :" . (sizeof($args) + 1); + array_push($args, $this->arg('year')); + } - if ($this->has_arg('scheduled')) { - $where .= " AND s.scheduled=1"; - } - - - if ($visit) { - $where .= " AND CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :".(sizeof($args)+1); - array_push($args, $visit); - } - - - if (!$this->staff) { - $where .= " AND shp.personid=:".(sizeof($args)+1); - array_push($args, $this->user->personid); + if ($this->has_arg('month')) { + $where .= " AND TO_CHAR(s.startdate, 'MM') = :" . (sizeof($args) + 1); + array_push($args, $this->arg('month')); + } + + if ($this->has_arg('prev')) { + $where .= " AND s.enddate < SYSDATE"; + } + + if ($this->has_arg('started')) { + $where .= " AND s.startdate < SYSDATE"; + } + + if ($this->has_arg('next')) { + $where .= " AND s.enddate > SYSDATE AND TO_CHAR(s.startdate,'YYYY') > 2009"; + $this->args['order'] = 'asc'; + $this->args['sort_by'] = 'ST'; + } + + if ($this->has_arg('bl')) { + $where .= " AND s.beamlinename = :" . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } + + if ($this->has_arg('cm')) { + $where .= " AND p.proposalcode LIKE :" . (sizeof($args) + 1) . " AND s.startdate <= SYSDATE"; + array_push($args, $commissioning_code); + } + + if ($this->has_arg('ty')) { + $beamlines = $this->_get_beamlines_from_type($this->arg('ty')); + + if (!empty($beamlines)) { + $bls = implode("', '", $beamlines); + $where .= " AND s.beamlinename IN ('$bls')"; } - - $tot = $this->db->pq("SELECT count(distinct s.sessionid) as tot + } + + if ($this->has_arg('s')) { + $where .= " AND s.visit_number LIKE :" . (sizeof($args) + 1); + array_push($args, $this->arg('s')); + } + + if ($this->has_arg('scheduled')) { + $where .= " AND s.scheduled=1"; + } + + if ($visit) { + $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :" . (sizeof($args) + 1); + array_push($args, $visit); + } + + if (!$this->staff) { + $where .= " AND shp.personid=:" . (sizeof($args) + 1); + array_push($args, $this->user->personId); + } + + $tot = $this->db->pq("SELECT count(distinct s.sessionid) as tot FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid LEFT OUTER JOIN session_has_person shp ON shp.sessionid = s.sessionid $where", $args); - $tot = intval($tot[0]['TOT']); - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - array_push($args, $start); - array_push($args, $end); - - $order = 's.startdate DESC'; - - if ($this->has_arg('sort_by')) { - $cols = array('ST' => 's.startdate', 'EN' => 's.enddate', 'VIS' => 's.visit_number', 'BL' => 's.beamlinename', 'LC' => 's.beamlineoperator', 'COMMENT' =>'s.comments'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } + $tot = intval($tot[0]['TOT']); + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + array_push($args, $start); + array_push($args, $end); - $rows = $this->db->paginate(" + $order = 's.startdate DESC'; + + if ($this->has_arg('sort_by')) { + $cols = array('ST' => 's.startdate', 'EN' => 's.enddate', 'VIS' => 's.visit_number', 'BL' => 's.beamlinename', 'LC' => 's.beamlineoperator', 'COMMENT' => 's.comments'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + $rows = $this->db->paginate(" SELECT CURRENT_TIMESTAMP BETWEEN s.startdate AND s.enddate AS active, CURRENT_TIMESTAMP BETWEEN DATE_SUB(s.startdate, INTERVAL 20 MINUTE) AND @@ -443,240 +475,293 @@ function _get_visits($visit=null, $output=true) { GROUP BY s.sessionid ORDER BY $order", $args); - $ids = array(); - $wcs = array(); - foreach ($rows as $r) { - array_push($ids, $r['SESSIONID']); - array_push($wcs, 'dcg.sessionid=:'.sizeof($ids)); - } - - $dcs = array(); - if (sizeof($ids)) { - $where = implode(' OR ', $wcs); - $tdcs = $this->db->pq("SELECT count(dc.datacollectionid) as c, dcg.sessionid + $ids = array(); + $wcs = array(); + foreach ($rows as $r) { + array_push($ids, $r['SESSIONID']); + array_push($wcs, 'dcg.sessionid=:' . sizeof($ids)); + } + + $dcs = array(); + if (sizeof($ids)) { + $where = implode(' OR ', $wcs); + $tdcs = $this->db->pq("SELECT count(dc.datacollectionid) as c, dcg.sessionid FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid WHERE $where GROUP BY dcg.sessionid", $ids); - foreach($tdcs as $t) $dcs[$t['SESSIONID']] = $t['C']; - } - + foreach ($tdcs as $t) + $dcs[$t['SESSIONID']] = $t['C']; + } - foreach ($rows as &$r) { - $dc = array_key_exists($r['SESSIONID'], $dcs) ? $dcs[$r['SESSIONID']] : 0; - $r['COMMENT'] = $r['COMMENTS']; - $r['DCCOUNT'] = $dc; + foreach ($rows as &$r) { + $dc = array_key_exists($r['SESSIONID'], $dcs) ? $dcs[$r['SESSIONID']] : 0; + $r['COMMENT'] = $r['COMMENTS']; + $r['DCCOUNT'] = $dc; - $bl_type = $this->_get_type_from_beamline($r['BL']); + $bl_type = $this->_get_type_from_beamline($r['BL']); - $r['TYPE'] = $bl_type ? $bl_type : 'gen'; - } - - if ($output) { - if ($visit) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such visit'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); - } else return $rows; + $r['TYPE'] = $bl_type ? $bl_type : 'gen'; } - - - # ------------------------------------------------------------------------ - # Get current visits - function _current_visits() { - unset($this->args['current']); - - $beamlines = $this->_get_beamlines_from_type($this->ty); - // The proposal type is synonymous with beamline type/group - if (empty($beamlines)) $this->_error('No such proposal type'); - - $this->args['per_page'] = 1; - $this->args['page'] = 1; - $this->args['all'] = 1; - - $rows = array(); - foreach (array('next', 'prev', 'cm') as $t) { - unset($this->args['order']); - unset($this->args['sort_by']); - foreach (array('next', 'prev', 'cm') as $r) unset($this->args[$r]); - $this->args[$t] = True; - if ($t == 'cm') unset($this->args['scheduled']); - else $this->args['scheduled'] = 1; - - foreach ($beamlines as $bl) { - $this->args['bl'] = $bl; - $vis = $this->_get_visits(null, False); - - if (sizeof($vis)) { - $vis[0]['VISIT-TYPE'] = $vis[0]['VISIT'].'-'.$t; - $vis[0]['type'] = $t; - array_push($rows, $vis[0]); - } + + if ($output) { + if ($visit) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such visit'); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } else + return $rows; + } + + + # ------------------------------------------------------------------------ + # Get current visits + function _current_visits() + { + unset($this->args['current']); + + $beamlines = $this->_get_beamlines_from_type($this->ty); + // The proposal type is synonymous with beamline type/group + if (empty($beamlines)) + $this->_error('No such proposal type'); + + $this->args['per_page'] = 1; + $this->args['page'] = 1; + $this->args['all'] = 1; + + $rows = array(); + foreach (array('next', 'prev', 'cm') as $t) { + unset($this->args['order']); + unset($this->args['sort_by']); + foreach (array('next', 'prev', 'cm') as $r) + unset($this->args[$r]); + $this->args[$t] = True; + if ($t == 'cm') + unset($this->args['scheduled']); + else + $this->args['scheduled'] = 1; + + foreach ($beamlines as $bl) { + $this->args['bl'] = $bl; + $vis = $this->_get_visits(null, False); + + if (sizeof($vis)) { + $vis[0]['VISIT-TYPE'] = $vis[0]['VISIT'] . '-' . $t; + $vis[0]['type'] = $t; + array_push($rows, $vis[0]); } } - - $this->_output(array('total' => sizeof($rows), 'data' => $rows)); } + $this->_output(array('total' => sizeof($rows), 'data' => $rows)); + } - # ------------------------------------------------------------------------ - # Update visit - function _update_visit() { - if (!$this->has_arg('visit')) $this->_error('No visit specified'); - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - $vis = $this->db->pq("SELECT s.sessionid, st.sessiontypeid, st.typename from blsession s - INNER JOIN proposal p ON p.proposalid = s.proposalid - LEFT OUTER JOIN sessiontype st on st.sessionid = s.sessionid - WHERE p.proposalid = :1 AND CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :2", array($this->proposalid, $this->arg('visit'))); - - if (!sizeof($vis)) $this->_error('No such visit'); - $vis = $vis[0]; - - $fields = array('COMMENTS'); - if ($this->user->can('manage_visits')) { - $fields = array_merge($fields, array('STARTDATE', 'ENDDATE', 'BEAMLINENAME', 'BEAMLINEOPERATOR', 'SCHEDULED', 'ARCHIVED', 'BEAMLINESETUPID', 'BEAMCALENDARID')); - } - foreach ($fields as $f) { - $fl = in_array($f, array('STARTDATE', 'ENDDATE')) ? "TO_DATE(:1, 'DD-MM-YYYY HH24:MI')" : ':1'; - if ($this->has_arg($f)) { - $this->db->pq("UPDATE blsession set $f=$fl where sessionid=:2", array($this->arg($f), $vis['SESSIONID'])); - $this->_output(array($f => $this->arg($f))); - } - } + # ------------------------------------------------------------------------ + # Update visit comment + function _update_visit_comment() + { + if (!$this->has_arg('visit')) $this->_error('No visit specified'); + if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if ($this->user->can('manage_visits')) { - if ($this->has_arg('SESSIONTYPE')) { - // Does this session already have a session type recorded? - if ($vis['SESSIONTYPEID']) { - // If so update the session type only if it's different (case sensitive check currently) - if (strcmp($vis['TYPENAME'], $this->arg('SESSIONTYPE')) !== 0) { - $this->db->pq("UPDATE sessiontype SET typename=:1 WHERE sessiontypeid=:2", array($this->arg('SESSIONTYPE'), $vis['SESSIONTYPEID'])); - } - } else { - $this->db->pq("INSERT INTO sessiontype (sessionid, typename) VALUES (:1, :2)", array($vis['SESSIONID'], $this->arg('SESSIONTYPE'))); - } + $vis = $this->db->pq("SELECT s.sessionid, st.sessiontypeid, st.typename from blsession s + INNER JOIN proposal p ON p.proposalid = s.proposalid + LEFT OUTER JOIN sessiontype st on st.sessionid = s.sessionid + WHERE p.proposalid = :1 AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :2", array($this->proposalid, $this->arg('visit'))); - $this->_output(array('SESSIONTYPE' => $this->arg('SESSIONTYPE'))); - } - } + if (!sizeof($vis)) $this->_error('No such visit'); + $vis = $vis[0]; + + + if ($this->has_arg('COMMENTS')) { + $this->db->pq("UPDATE blsession set comments=:1 where sessionid=:2", array($this->arg('COMMENTS'), $vis['SESSIONID'])); + $this->_output(array('COMMENTS' => $this->arg('COMMENTS'))); } + } + + # ------------------------------------------------------------------------ + # Update visit + function _update_visit() + { + if (!$this->has_arg('visit')) + $this->_error('No visit specified'); + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - # ------------------------------------------------------------------------ - # Add visit - function _add_visit() { - if (!$this->user->can('manage_visits')) $this->_error('No access'); + $vis = $this->db->pq("SELECT s.sessionid, st.sessiontypeid, st.typename from blsession s + INNER JOIN proposal p ON p.proposalid = s.proposalid + LEFT OUTER JOIN sessiontype st on st.sessionid = s.sessionid + WHERE p.proposalid = :1 AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :2", array($this->proposalid, $this->arg('visit'))); - if (!$this->has_arg('PROPOSALID')) $this->_error('No proposal specified'); - if (!$this->has_arg('STARTDATE')) $this->_error('No start date specified'); - if (!$this->has_arg('ENDDATE')) $this->_error('No end date specified'); - if (!$this->has_arg('BEAMLINENAME')) $this->_error('No beamline specified'); - if (!$this->has_arg('BEAMLINEOPERATOR')) $this->_error('No beamline operator specified'); + if (!sizeof($vis)) + $this->_error('No such visit'); + $vis = $vis[0]; - $sch = $this->has_arg('SCHEDULED') ? $this->arg('SCHEDULED') : 1; - $arc = $this->has_arg('ARCHIVED') ? $this->arg('ARCHIVED') : 0; - $extid = $this->has_arg('EXTERNALID') ? $this->arg('EXTERNALID') : null; - $blsid = $this->has_arg('BEAMLINESETUPID') ? $this->arg('BEAMLINESETUPID') : null; - $calid = $this->has_arg('BEAMCALENDARID') ? $this->arg('BEAMCALENDARID') : null; + $fields = array('COMMENTS'); + $this->haltIfLackingPermission('manage_visits'); - if ($this->has_arg('VISITNUMBER')) { - // Does this visit already exist? If so, throw an error. - $chk = $this->db->pq("SELECT sessionid FROM blsession WHERE proposalid = :1 AND visit_number=:2", array($this->arg('PROPOSALID'), $this->arg('VISITNUMBER'))); - if (sizeof($chk)) $this->_error('Error - visit number ' . $this->arg('VISITNUMBER') . ' already exists for the proposal'); + $fields = array_merge($fields, array('STARTDATE', 'ENDDATE', 'BEAMLINENAME', 'BEAMLINEOPERATOR', 'SCHEDULED', 'ARCHIVED', 'BEAMLINESETUPID', 'BEAMCALENDARID')); + + foreach ($fields as $f) { + $fl = in_array($f, array('STARTDATE', 'ENDDATE')) ? "TO_DATE(:1, 'DD-MM-YYYY HH24:MI')" : ':1'; + if ($this->has_arg($f)) { + $this->db->pq("UPDATE blsession set $f=$fl where sessionid=:2", array($this->arg($f), $vis['SESSIONID'])); + $this->_output(array($f => $this->arg($f))); } - $max = $this->db->pq("SELECT MAX(visit_number) as max_visit FROM blsession WHERE proposalid=:1", array($this->arg('PROPOSALID'))); - // If a visit number has been specified use that, else use the next value - $vis = $this->has_arg('VISITNUMBER') ? $this->arg('VISITNUMBER') : $max[0]['MAX_VISIT'] + 1; + } - $this->db->pq("INSERT INTO blsession (proposalid, startdate, enddate, beamlinename, beamlineoperator, scheduled, visit_number, externalid, archived, beamlinesetupid, beamcalendarid) - VALUES (:1, TO_DATE(:2, 'DD-MM-YYYY HH24:MI'), TO_DATE(:3, 'DD-MM-YYYY HH24:MI'), :4, :5, :6, :7, :8, :9, :10, :11)", - array($this->arg('PROPOSALID'), $this->arg('STARTDATE'), $this->arg('ENDDATE'), $this->arg('BEAMLINENAME'), $this->arg('BEAMLINEOPERATOR'), $sch, $vis, $extid, $arc, $blsid, $calid)); + $this->haltIfLackingPermission('manage_visits'); + if ($this->has_arg('SESSIONTYPE')) { + // Does this session already have a session type recorded? + if ($vis['SESSIONTYPEID']) { + // If so update the session type only if it's different (case sensitive check currently) + if (strcmp($vis['TYPENAME'], $this->arg('SESSIONTYPE')) !== 0) { + $this->db->pq("UPDATE sessiontype SET typename=:1 WHERE sessiontypeid=:2", array($this->arg('SESSIONTYPE'), $vis['SESSIONTYPEID'])); + } + } else { + $this->db->pq("INSERT INTO sessiontype (sessionid, typename) VALUES (:1, :2)", array($vis['SESSIONID'], $this->arg('SESSIONTYPE'))); + } - $id = $this->db->id(); + $this->_output(array('SESSIONTYPE' => $this->arg('SESSIONTYPE'))); + } + } + + + # ------------------------------------------------------------------------ + # Add visit + function _add_visit() + { + !$this->haltIfLackingPermission('manage_visits'); + + if (!$this->has_arg('PROPOSALID')) + $this->_error('No proposal specified'); + if (!$this->has_arg('STARTDATE')) + $this->_error('No start date specified'); + if (!$this->has_arg('ENDDATE')) + $this->_error('No end date specified'); + if (!$this->has_arg('BEAMLINENAME')) + $this->_error('No beamline specified'); + if (!$this->has_arg('BEAMLINEOPERATOR')) + $this->_error('No beamline operator specified'); + + $sch = $this->has_arg('SCHEDULED') ? $this->arg('SCHEDULED') : 1; + $arc = $this->has_arg('ARCHIVED') ? $this->arg('ARCHIVED') : 0; + $extid = $this->has_arg('EXTERNALID') ? $this->arg('EXTERNALID') : null; + $blsid = $this->has_arg('BEAMLINESETUPID') ? $this->arg('BEAMLINESETUPID') : null; + $calid = $this->has_arg('BEAMCALENDARID') ? $this->arg('BEAMCALENDARID') : null; + + if ($this->has_arg('VISITNUMBER')) { + // Does this visit already exist? If so, throw an error. + $chk = $this->db->pq("SELECT sessionid FROM blsession WHERE proposalid = :1 AND visit_number=:2", array($this->arg('PROPOSALID'), $this->arg('VISITNUMBER'))); + if (sizeof($chk)) + $this->_error('Error - visit number ' . $this->arg('VISITNUMBER') . ' already exists for the proposal'); + } + $max = $this->db->pq("SELECT MAX(visit_number) as max_visit FROM blsession WHERE proposalid=:1", array($this->arg('PROPOSALID'))); + // If a visit number has been specified use that, else use the next value + $vis = $this->has_arg('VISITNUMBER') ? $this->arg('VISITNUMBER') : $max[0]['MAX_VISIT'] + 1; + + $this->db->pq( + "INSERT INTO blsession (proposalid, startdate, enddate, beamlinename, beamlineoperator, scheduled, visit_number, externalid, archived, beamlinesetupid, beamcalendarid) + VALUES (:1, TO_DATE(:2, 'DD-MM-YYYY HH24:MI'), TO_DATE(:3, 'DD-MM-YYYY HH24:MI'), :4, :5, :6, :7, :8, :9, :10, :11)", + array($this->arg('PROPOSALID'), $this->arg('STARTDATE'), $this->arg('ENDDATE'), $this->arg('BEAMLINENAME'), $this->arg('BEAMLINEOPERATOR'), $sch, $vis, $extid, $arc, $blsid, $calid) + ); - $visit = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit + $id = $this->db->id(); + + $visit = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE s.sessionid = :1", array($id)); - $this->_output(array( - 'BLSESSIONID' => $id, - 'VISIT' => $visit[0]['VISIT'], - )); - } + $this->_output(array( + 'BLSESSIONID' => $id, + 'VISIT' => $visit[0]['VISIT'], + )); + } - # ------------------------------------------------------------------------ - # Add user to a visit - function _add_visit_user() { - if (!$this->user->can('manage_vusers')) $this->_error('No access'); - if (!$this->has_arg('PERSONID')) $this->_error('No person specified'); - if (!$this->has_arg('SESSIONID')) $this->_error('No visit specified'); + # ------------------------------------------------------------------------ + # Add user to a visit + function _add_visit_user() + { + $this->haltIfLackingPermission('manage_vusers'); - $user = $this->db->pq("SELECT personid FROM person where personid=:1", array($this->arg('PERSONID'))); - if (!sizeof($user)) $this->_error('The specified person doesnt exist'); + if (!$this->has_arg('PERSONID')) + $this->_error('No person specified'); + if (!$this->has_arg('SESSIONID')) + $this->_error('No visit specified'); - $visit = $this->db->pq("SELECT sessionid FROM blsession WHERE sessionid=:1", array($this->arg('SESSIONID'))); - if (!sizeof($user)) $this->_error('The specified visit doesnt exist'); + $user = $this->db->pq("SELECT personid FROM person where personid=:1", array($this->arg('PERSONID'))); + if (!sizeof($user)) + $this->_error('The specified person doesnt exist'); - $chk = $this->db->pq("SELECT shp.role + $visit = $this->db->pq("SELECT sessionid FROM blsession WHERE sessionid=:1", array($this->arg('SESSIONID'))); + if (!sizeof($user)) + $this->_error('The specified visit doesnt exist'); + + $chk = $this->db->pq("SELECT shp.role FROM session_has_person shp WHERE sessionid=:1 and personid=:2", array($this->arg('SESSIONID'), $this->arg('PERSONID'))); - if (sizeof($chk)) { - $this->_error('That user is already registered on the specified visit'); - } + if (sizeof($chk)) { + $this->_error('That user is already registered on the specified visit'); + } - $role = $this->has_arg("ROLE") ? $this->arg("ROLE") : 'Team Member'; - $remote = $this->has_arg("REMOTE") ? $this->arg("REMOTE") : 0; + $role = $this->has_arg("ROLE") ? $this->arg("ROLE") : 'Team Member'; + $remote = $this->has_arg("REMOTE") ? $this->arg("REMOTE") : 0; - $this->db->pq("INSERT INTO session_has_person (sessionid, personid, role, remote) + $this->db->pq("INSERT INTO session_has_person (sessionid, personid, role, remote) VALUES (:1, :2, :3, :4)", array($this->arg("SESSIONID"), $this->arg("PERSONID"), $role, $remote)); - $this->_output(array( - 'SHPKEY' => $this->arg("SESSIONID").'-'.$this->arg("PERSONID"), - )); - } + $this->_output(array( + 'SHPKEY' => $this->arg("SESSIONID") . '-' . $this->arg("PERSONID"), + )); + } - # ------------------------------------------------------------------------ - # Get visit users - # duplication from class.users.php :( - function _get_visit_users() { - if (!$this->user->can('manage_vusers')) $this->_error('No access'); + # ------------------------------------------------------------------------ + # Get visit users + # duplication from class.users.php :( + function _get_visit_users() + { + $this->haltIfLackingPermission('manage_vusers'); - $where = '1=1'; - $args = array(); + $where = '1=1'; + $args = array(); - if ($this->has_arg('SHPKEY')) { - list($sessionid, $personid) = split('-', $this->arg('SHPKEY')); - $where .= " shp.sessionid=:1 AND shp.personid=:2"; - array_push($args, $sessionid); - array_push($args, $personid); - } + if ($this->has_arg('SHPKEY')) { + list($sessionid, $personid) = explode('-', $this->arg('SHPKEY')); + $where .= " shp.sessionid=:1 AND shp.personid=:2"; + array_push($args, $sessionid); + array_push($args, $personid); + } - if ($this->has_arg('visit')) { - $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1"; - array_push($args, $this->arg('visit')); - } + if ($this->has_arg('visit')) { + $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1"; + array_push($args, $this->arg('visit')); + } - $tot = $this->db->pq("SELECT count(shp.personid) as tot + $tot = $this->db->pq("SELECT count(shp.personid) as tot FROM session_has_person shp INNER JOIN blsession s ON s.sessionid = shp.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('PERSONID' => 'shp.personid', 'SESSIONID' => 'shp.sessionid'), - 'shp.personid DESC' - ); + $order = $this->_get_order( + array('PERSONID' => 'shp.personid', 'SESSIONID' => 'shp.sessionid'), + 'shp.personid DESC' + ); - $rows = $this->db->paginate("SELECT shp.personid, shp.sessionid, shp.role, shp.remote, CONCAT(shp.sessionid, '-', pe.personid) as shpkey, CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as fullname + $rows = $this->db->paginate("SELECT shp.personid, shp.sessionid, shp.role, shp.remote, CONCAT(shp.sessionid, '-', pe.personid) as shpkey, CONCAT(pe.givenname, ' ', pe.familyname) as fullname FROM session_has_person shp INNER JOIN person pe ON pe.personid = shp.personid INNER JOIN blsession s ON s.sessionid = shp.sessionid @@ -684,156 +769,167 @@ function _get_visit_users() { WHERE $where ", $args); - if ($this->has_arg('SHPKEY')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such person on that session'); - - } else $this->_output(array( + if ($this->has_arg('SHPKEY')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such person on that session'); + } else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } - - # ------------------------------------------------------------------------ - # Update a user on a visit - function _update_visit_user() { - if (!$this->user->can('manage_vusers')) $this->_error('No access'); - list($sessionid, $personid) = split('-', $this->arg('SHPKEY')); - - $chk = $this->db->pq("SELECT personid, sessionid FROM session_has_person WHERE sessionid=:1 AND personid=:2", array($sessionid, $personid)); - if (!sizeof($chk)) $this->_error('The specified user is not registered on that visit'); - - $fields = array('ROLE', 'REMOTE'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE session_has_person set $f=:1 where sessionid=:2 and personid=:3", array($this->arg($f), $sessionid, $personid)); - $this->_output(array($f => $this->arg($f))); - } + } + + # ------------------------------------------------------------------------ + # Update a user on a visit + function _update_visit_user() + { + $this->haltIfLackingPermission('manage_vusers'); + list($sessionid, $personid) = explode('-', $this->arg('SHPKEY')); + + $chk = $this->db->pq("SELECT personid, sessionid FROM session_has_person WHERE sessionid=:1 AND personid=:2", array($sessionid, $personid)); + if (!sizeof($chk)) + $this->_error('The specified user is not registered on that visit'); + + $fields = array('ROLE', 'REMOTE'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE session_has_person set $f=:1 where sessionid=:2 and personid=:3", array($this->arg($f), $sessionid, $personid)); + $this->_output(array($f => $this->arg($f))); } } + } - # ------------------------------------------------------------------------ - # Remove user from a visit - function _remove_visit_user() { - if (!$this->user->can('manage_vusers')) $this->_error('No access'); - list($sessionid, $personid) = split('-', $this->arg('SHPKEY')); + # ------------------------------------------------------------------------ + # Remove user from a visit + function _remove_visit_user() + { + $this->haltIfLackingPermission('manage_vusers'); + list($sessionid, $personid) = explode('-', $this->arg('SHPKEY')); - $chk = $this->db->pq("SELECT personid FROM session_has_person WHERE sessionid=:1 AND personid=:2", array($sessionid, $personid)); - if (!sizeof($chk)) $this->_error('The specified user is not registered on that visit'); + $chk = $this->db->pq("SELECT personid FROM session_has_person WHERE sessionid=:1 AND personid=:2", array($sessionid, $personid)); + if (!sizeof($chk)) + $this->_error('The specified user is not registered on that visit'); - $this->db->pq("DELETE FROM session_has_person + $this->db->pq("DELETE FROM session_has_person WHERE sessionid=:1 and personid=:2", array($sessionid, $personid)); - $this->_output(1); - } + $this->_output(1); + } - # ------------------------------------------------------------------------ - # Get beam calendar - function _get_beam_calendar() { - $where = '1=1'; - $args = array(); + # ------------------------------------------------------------------------ + # Get beam calendar + function _get_beam_calendar() + { + $where = '1=1'; + $args = array(); - if ($this->has_arg('BEAMCALENDARID')) { - $where .= ' AND bc.beamcalendarid = :'.(sizeof($args)+1); - array_push($args, $this->arg('BEAMCALENDARID')); - } + if ($this->has_arg('BEAMCALENDARID')) { + $where .= ' AND bc.beamcalendarid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('BEAMCALENDARID')); + } - if ($this->has_arg('STARTDATE')) { - $where .= ' AND bc.startdate >= TO_DATE(:'.(sizeof($args)+1)+')'; - array_push($args, $this->arg('STARTDATE')); - } + if ($this->has_arg('STARTDATE')) { + $where .= ' AND bc.startdate >= TO_DATE(:' . (sizeof($args) + 1) + ')'; + array_push($args, $this->arg('STARTDATE')); + } - if ($this->has_arg('ENDDATE')) { - $where .= ' AND bc.enddate <= TO_DATE(:'.(sizeof($args)+1)+')'; - array_push($args, $this->arg('ENDDATE')); - } + if ($this->has_arg('ENDDATE')) { + $where .= ' AND bc.enddate <= TO_DATE(:' . (sizeof($args) + 1) + ')'; + array_push($args, $this->arg('ENDDATE')); + } - $tot = $this->db->pq("SELECT count(bc.beamcalendarid) as tot + $tot = $this->db->pq("SELECT count(bc.beamcalendarid) as tot FROM beamcalendar bc WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $this->_get_start_end($args); + $this->_get_start_end($args); - $order = $this->_get_order( - array('BEAMCALENDARID' => 'bc.beamcalendarid'), - 'bls.beamlinesetupid DESC' - ); + $order = $this->_get_order( + array('BEAMCALENDARID' => 'bc.beamcalendarid'), + 'bls.beamlinesetupid DESC' + ); - $rows = $this->db->paginate("SELECT bc.beamcalendarid, bc.run, TO_CHAR(bc.startdate, 'DD-MM-YYYY') as startdate, TO_CHAR(bc.enddate, 'DD-MM-YYYY') as enddate, count(s.sessionid) as sessions + $rows = $this->db->paginate("SELECT bc.beamcalendarid, bc.run, TO_CHAR(bc.startdate, 'DD-MM-YYYY') as startdate, TO_CHAR(bc.enddate, 'DD-MM-YYYY') as enddate, count(s.sessionid) as sessions FROM beamcalendar bc LEFT OUTER JOIN blsession s ON s.beamcalendarid = bc.beamcalendarid WHERE $where GROUP BY bc.beamcalendarid ", $args); - if ($this->has_arg('BEAMCALENDARID')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such beam calendar'); - - } else $this->_output(array( + if ($this->has_arg('BEAMCALENDARID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such beam calendar'); + } else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } - - # ------------------------------------------------------------------------ - # Lookup visit from container, dewar, sample, etc, ... - function _lookup() { - - $fields = array( - 'BLSAMPLEID' => 's.blsampleid', - 'CRYSTALID' => 'cr.crystalid', - 'PROTEINID' => 'pr.proteinid', - 'CONTAINERID' => 'c.containerid', - 'DEWARID' => 'd.dewarid', - 'SHIPPINGID' => 'sh.shippingid', - 'LABCONTACTID' => 'lc.labcontactid', - 'DATACOLLECTIONID' => 'dc.datacollectionid', - ); + } + + # ------------------------------------------------------------------------ + # Lookup visit from container, dewar, sample, etc, ... + function _lookup() + { + + $fields = array( + 'BLSAMPLEID' => 's.blsampleid', + 'CRYSTALID' => 'cr.crystalid', + 'PROTEINID' => 'pr.proteinid', + 'CONTAINERID' => 'c.containerid', + 'DEWARID' => 'd.dewarid', + 'SHIPPINGID' => 'sh.shippingid', + 'LABCONTACTID' => 'lc.labcontactid', + 'DATACOLLECTIONID' => 'dc.datacollectionid', + ); - $field = null; - foreach ($fields as $f => $v) { - if ($this->has_arg($f)) { - $field = $f; - break; - } + $field = null; + foreach ($fields as $f => $v) { + if ($this->has_arg($f)) { + $field = $f; + break; } + } - if (!$field) $this->_error('No id specified'); + if (!$field) + $this->_error('No id specified'); - $where = "WHERE 1=1"; - $args = array(); + $where = "WHERE 1=1"; + $args = array(); - if ($this->staff) { - if (!$this->user->has('super_admin')) { - $bls = array(); - foreach ($this->user->perms as $p) { - if (strpos($p, '_admin')) { - $parts = explode('_', $p); - $ty = $parts[0]; - // _get_beamlines_from_type returns an empty array if type not found, so we can just merge.... - $bls = array_merge($bls, $this->_get_beamlines_from_type($ty)); - } + if ($this->staff) { + if (!$this->user->hasPermission('super_admin')) { + $bls = array(); + foreach ($this->user->perms as $p) { + if (strpos($p, '_admin')) { + $parts = explode('_', $p); + $ty = $parts[0]; + // _get_beamlines_from_type returns an empty array if type not found, so we can just merge.... + $bls = array_merge($bls, $this->_get_beamlines_from_type($ty)); } - - $where .= " AND ses.beamlinename in ('".implode("','", $bls)."')"; } - } else { - $where = " INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid ".$where; - $where .= " AND shp.personid=:".(sizeof($args)+1); - array_push($args, $this->user->personid); + + $where .= " AND ses.beamlinename in ('" . implode("','", $bls) . "')"; } + } else { + $where = " INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid " . $where; + $where .= " AND shp.personid=:" . (sizeof($args) + 1); + array_push($args, $this->user->personId); + } - $where .= ' AND '.$fields[$field].'=:'.(sizeof($args)+1); - array_push($args, $this->arg($field)); + $where .= ' AND ' . $fields[$field] . '=:' . (sizeof($args) + 1); + array_push($args, $this->arg($field)); - // $this->db->set_debug(True); - $rows = $this->db->pq("SELECT distinct CONCAT(p.proposalcode, p.proposalnumber) as prop + // $this->db->set_debug(True); + $rows = $this->db->pq("SELECT distinct CONCAT(p.proposalcode, p.proposalnumber) as prop FROM proposal p LEFT OUTER JOIN blsession ses ON ses.proposalid = p.proposalid LEFT OUTER JOIN datacollectiongroup dcg ON dcg.sessionid = ses.sessionid @@ -852,39 +948,43 @@ function _lookup() { ", $args); - if (sizeof($rows)) { - $this->_output($rows[0]); - } else { - $this->_error('No such proposal'); - } - + if (sizeof($rows)) { + $this->_output($rows[0]); + } else { + $this->_error('No such proposal'); } - - - - # ------------------------------------------------------------------------ - # Create visit for autocollect - /** - * Controller method for auto collect. - * This function will be called when pucks are scanned into a sample changer. - * First time a puck is scanned we create a new session. - * If there is already an active Auto Collect session for the beamline then add the container to the existing session. - * Containers are added to the autocollect session if they are on the same shipment to provide some sensible aggregation. - * Access is restricted to ip addresses within an "auto" list from config.php and certain beamlines 'auto_bls'. - * - */ - function _auto_visit() { - global $auto, $auto_bls, $auto_exp_hazard, $auto_sample_hazard, $auto_user, $auto_pass, $auto_session_type; - - if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) $this->_error('You do not have access to that resource', 401); - - if (!$this->has_arg('CONTAINERID')) $this->_error('No container specified'); - if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - if (!in_array($this->arg('bl'), $auto_bls)) $this->_error('That beamline cannot create autocollect visits', 401); - - // Get container information - note that if the container has no owner - we use the proposal person. - // A person record is required for UAS so we can set investigators. UAS needs one 'TEAM_LEADER' and others as 'DATA_ACCESS' - $cont = $this->db->pq("SELECT c.sessionid, p.proposalid, ses.visit_number, CONCAT(p.proposalcode, p.proposalnumber) as proposal, + } + + + + # ------------------------------------------------------------------------ + # Create visit for autocollect + /** + * Controller method for auto collect. + * This function will be called when pucks are scanned into a sample changer. + * First time a puck is scanned we create a new session. + * If there is already an active Auto Collect session for the beamline then add the container to the existing session. + * Containers are added to the autocollect session if they are on the same shipment to provide some sensible aggregation. + * Access is restricted to ip addresses within an "auto" list from config.php and certain beamlines 'auto_bls'. + * + */ + function _auto_visit() + { + global $auto, $auto_bls, $auto_exp_hazard, $auto_sample_hazard, $auto_user, $auto_pass, $auto_session_type; + + if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) + $this->_error('You do not have access to that resource', 401); + + if (!$this->has_arg('CONTAINERID')) + $this->_error('No container specified'); + if (!$this->has_arg('bl')) + $this->_error('No beamline specified'); + if (!in_array($this->arg('bl'), $auto_bls)) + $this->_error('That beamline cannot create autocollect visits', 401); + + // Get container information - note that if the container has no owner - we use the proposal person. + // A person record is required for UAS so we can set investigators. UAS needs one 'TEAM_LEADER' and others as 'DATA_ACCESS' + $cont = $this->db->pq("SELECT c.sessionid, p.proposalid, ses.visit_number, CONCAT(p.proposalcode, p.proposalnumber) as proposal, c.containerid, HEX(p.externalid) as externalid, pe.personid as ownerid, @@ -901,35 +1001,36 @@ function _auto_visit() { LEFT OUTER JOIN person pe2 ON pe2.personid = p.personid WHERE c.containerid=:1", array($this->arg('CONTAINERID'))); - if (!sizeof($cont)) $this->_error('No such container', 404); - - // Store container info for convenience - $cont = $cont[0]; - - // Check if the container owner is a valid person (in User Office) - // If not try and use the Proposal/Principal Investigator - if ($cont['OWNERID'] && $cont['OWNEREXTERNALID']) { - $cont['PEXTERNALID'] = $cont['OWNEREXTERNALID']; - $cont['PERSONID'] = $cont['OWNERID']; - } else { - if ($cont['PIID'] && $cont['PIEXTERNALID']) { - $cont['PEXTERNALID'] = $cont['PIEXTERNALID']; - $cont['PERSONID'] = $cont['PIID']; - } + if (!sizeof($cont)) + $this->_error('No such container', 404); + + // Store container info for convenience + $cont = $cont[0]; + + // Check if the container owner is a valid person (in User Office) + // If not try and use the Proposal/Principal Investigator + if ($cont['OWNERID'] && $cont['OWNEREXTERNALID']) { + $cont['PEXTERNALID'] = $cont['OWNEREXTERNALID']; + $cont['PERSONID'] = $cont['OWNERID']; + } else { + if ($cont['PIID'] && $cont['PIEXTERNALID']) { + $cont['PEXTERNALID'] = $cont['PIEXTERNALID']; + $cont['PERSONID'] = $cont['PIID']; } + } - if (!$cont['PEXTERNALID']) { - $this->_error('That container does not have a valid owner', 412); - } - if ($cont['SESSIONID']) { - error_log('That container already has a session ' . $cont['SESSIONID']); - - $this->_output(array('VISIT' => $cont['PROPOSAL'].'-'.$cont['VISIT_NUMBER'])); - } else { - // Is there an existing auto collect session for this beamline with containers on the same shipment? - // If so we add this container to the existing Auto Collect session. - // Proposal reference is used to capture the visit string to return later - $auto_sessions = $this->db->pq("SELECT ses.sessionid, HEX(ses.externalid) as sexternalid, ses.startDate, ses.endDate, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit + if (!$cont['PEXTERNALID']) { + $this->_error('That container does not have a valid owner', 412); + } + if ($cont['SESSIONID']) { + error_log('That container already has a session ' . $cont['SESSIONID']); + + $this->_output(array('VISIT' => $cont['PROPOSAL'] . '-' . $cont['VISIT_NUMBER'])); + } else { + // Is there an existing auto collect session for this beamline with containers on the same shipment? + // If so we add this container to the existing Auto Collect session. + // Proposal reference is used to capture the visit string to return later + $auto_sessions = $this->db->pq("SELECT ses.sessionid, HEX(ses.externalid) as sexternalid, ses.startDate, ses.endDate, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit FROM Container c INNER JOIN Dewar d ON d.dewarId = c.dewarId INNER JOIN Shipping sh ON sh.shippingId = d.shippingId @@ -941,197 +1042,203 @@ function _auto_visit() { AND sh.shippingid = :2 AND (CURRENT_TIMESTAMP BETWEEN ses.startDate AND ses.endDate)", array($this->arg('bl'), $cont['SHIPPINGID'])); - if(!sizeof($auto_sessions)) { - // Create new session - passing containerID, proposalID and UAS proposal ID - $sessionNumber = $this->_create_new_autocollect_session($cont['CONTAINERID'], $cont['PROPOSALID'], $cont['EXTERNALID'], $cont['PERSONID'], $cont['PEXTERNALID'] ); + if (!sizeof($auto_sessions)) { + // Create new session - passing containerID, proposalID and UAS proposal ID + $sessionNumber = $this->_create_new_autocollect_session($cont['CONTAINERID'], $cont['PROPOSALID'], $cont['EXTERNALID'], $cont['PERSONID'], $cont['PEXTERNALID']); - if ($sessionNumber > 0) { - $result = array('VISIT' => $cont['PROPOSAL'].'-'.$sessionNumber, 'CONTAINERS' => array($cont['CONTAINERID'])); + if ($sessionNumber > 0) { + $result = array('VISIT' => $cont['PROPOSAL'] . '-' . $sessionNumber, 'CONTAINERS' => array($cont['CONTAINERID'])); - $this->_output($result); - } else { - $this->_error('Something went wrong creating a session for that container ' . $cont['CONTAINERID']); - } + $this->_output($result); } else { - // Update existing session - passing Session ID, UAS Session ID, Container ID and the current Team Leader (so its preserved in UAS) - $auto_session = $auto_sessions[0]; + $this->_error('Something went wrong creating a session for that container ' . $cont['CONTAINERID']); + } + } else { + // Update existing session - passing Session ID, UAS Session ID, Container ID and the current Team Leader (so its preserved in UAS) + $auto_session = $auto_sessions[0]; - // Find the team leader for this auto collect session - $team_leader = $this->db->pq("SELECT shp.role, pe.personId as teamleaderId, HEX(pe.externalId) as teamleaderExtId + // Find the team leader for this auto collect session + $team_leader = $this->db->pq("SELECT shp.role, pe.personId as teamleaderId, HEX(pe.externalId) as teamleaderExtId FROM Session_has_Person shp INNER JOIN Person pe ON pe.personId = shp.personId WHERE shp.sessionId = :1 AND shp.role='Team Leader'", array($auto_session['SESSIONID'])); - if (!sizeof($team_leader)) { - error_log('Proposal::auto_session - no team leader for an existing Auto Collect Session'); - $this->_error('Precondition failed, no team leader role found while adding container ' . $cont['CONTAINERID'] . ' to session ' . $auto_session['SESSIONID'], 412); - } + if (!sizeof($team_leader)) { + error_log('Proposal::auto_session - no team leader for an existing Auto Collect Session'); + $this->_error('Precondition failed, no team leader role found while adding container ' . $cont['CONTAINERID'] . ' to session ' . $auto_session['SESSIONID'], 412); + } - $result = $this->_update_autocollect_session($auto_session['SESSIONID'], $auto_session['SEXTERNALID'], $cont['CONTAINERID'], $team_leader[0]['TEAMLEADEREXTID']); + $result = $this->_update_autocollect_session($auto_session['SESSIONID'], $auto_session['SEXTERNALID'], $cont['CONTAINERID'], $team_leader[0]['TEAMLEADEREXTID']); - if ($result) { - // Add visit to return value... - // Just returning number of samples and investigators along with container list - $resp['VISIT'] = $auto_session['VISIT']; - $resp['CONTAINERS'] = $result['CONTAINERS']; - $this->_output($resp); - } else { - $this->_error('Something went wrong adding container ' . $cont['CONTAINERID'] . ' to session ' . $auto_session['SESSIONID']); - } + if ($result) { + // Add visit to return value... + // Just returning number of samples and investigators along with container list + $resp['VISIT'] = $auto_session['VISIT']; + $resp['CONTAINERS'] = $result['CONTAINERS']; + $this->_output($resp); + } else { + $this->_error('Something went wrong adding container ' . $cont['CONTAINERID'] . ' to session ' . $auto_session['SESSIONID']); } } } - /** - * Create new auto collect session - * This function needs to find the samples and investigators for this container - * - * @param array $containerId ISPyB ContainerID - * @param array $proposalId ISPyB ProposalID - * @param array $uasProposalId UAS ProposalID (from Proposal.externalId) - * @return integer Returns session number generated from UAS - */ - function _create_new_autocollect_session($containerId, $proposalId, $uasProposalId, $personId, $uasPersonId) { - global $auto_exp_hazard, $auto_sample_hazard, $auto_user, $auto_pass, $auto_session_type; - // Return session number generated from UAS ( will be > 0 if OK) - $sessionNumber = 0; - // So now we need to create a new session in UAS and update ISPyB - // Get Samples info from current (new) container - $sampleInfo = $this->_get_valid_samples_from_containers(array($containerId)); - - if ($sampleInfo['INVESTIGATORS'] && $sampleInfo['SAMPLES']) { - // Set the first investigator as the team lead - $sampleInfo['INVESTIGATORS'][0]['role'] = 'TEAM_LEADER'; - - // Create new session..... - $data = array( - 'proposalId' => strtoupper($uasProposalId), - 'sampleIds' => array_values($sampleInfo['SAMPLES']), - 'startAt' => date('Y-m-d\TH:i:s.000\Z'), - 'facility' => strtoupper($this->arg('bl')), - 'investigators' => array_values($sampleInfo['INVESTIGATORS']), - 'experimentalMethods' => array(array( - 'state' => 'Submitted', - 'experimentHazard' => array('description' => $auto_exp_hazard), - 'preparationHazard' => array('description' => $auto_sample_hazard) - )), - 'eraState' => 'Submitted' - ); - // Create the session in UAS with our special autocollect user - $uas = new UAS($auto_user, $auto_pass); - $sess = $uas->create_session($data); - - if ($sess['code'] == 200 && $sess['resp']) { - // Set the initial end Date as two days from now - this will be updated by propagation from UAS later. - // Also the session endDate will be set once the samples are unloaded by calling the close_session endpoint. - $this->db->pq("INSERT INTO blsession (proposalid, visit_number, externalid, beamlinename, beamlinesetupid, startDate, endDate) + } + /** + * Create new auto collect session + * This function needs to find the samples and investigators for this container + * + * @param array $containerId ISPyB ContainerID + * @param array $proposalId ISPyB ProposalID + * @param array $uasProposalId UAS ProposalID (from Proposal.externalId) + * @return integer Returns session number generated from UAS + */ + function _create_new_autocollect_session($containerId, $proposalId, $uasProposalId, $personId, $uasPersonId) + { + global $auto_exp_hazard, $auto_sample_hazard, $auto_user, $auto_pass, $auto_session_type; + // Return session number generated from UAS ( will be > 0 if OK) + $sessionNumber = 0; + // So now we need to create a new session in UAS and update ISPyB + // Get Samples info from current (new) container + $sampleInfo = $this->_get_valid_samples_from_containers(array($containerId)); + + if ($sampleInfo['INVESTIGATORS'] && $sampleInfo['SAMPLES']) { + // Set the first investigator as the team lead + $sampleInfo['INVESTIGATORS'][0]['role'] = 'TEAM_LEADER'; + + // Create new session..... + $data = array( + 'proposalId' => strtoupper($uasProposalId), + 'sampleIds' => array_values($sampleInfo['SAMPLES']), + 'startAt' => date('Y-m-d\TH:i:s.000\Z'), + 'facility' => strtoupper($this->arg('bl')), + 'investigators' => array_values($sampleInfo['INVESTIGATORS']), + 'experimentalMethods' => array(array( + 'state' => 'Submitted', + 'experimentHazard' => array('description' => $auto_exp_hazard), + 'preparationHazard' => array('description' => $auto_sample_hazard) + )), + 'eraState' => 'Submitted' + ); + // Create the session in UAS with our special autocollect user + $uas = new UAS($auto_user, $auto_pass); + $sess = $uas->create_session($data); + + if ($sess['code'] == 200 && $sess['resp']) { + // Set the initial end Date as two days from now - this will be updated by propagation from UAS later. + // Also the session endDate will be set once the samples are unloaded by calling the close_session endpoint. + $this->db->pq( + "INSERT INTO blsession (proposalid, visit_number, externalid, beamlinename, beamlinesetupid, startDate, endDate) VALUES (:1,:2,UNHEX(:3),:4,1, CURRENT_TIMESTAMP, TIMESTAMPADD(DAY,2,CURRENT_TIMESTAMP))", - array($proposalId, $sess['resp']->sessionNumber, $sess['resp']->id, $this->arg('bl'))); - - $sessionId = $this->db->id(); + array($proposalId, $sess['resp']->sessionNumber, $sess['resp']->id, $this->arg('bl')) + ); - $this->db->pq("INSERT INTO sessiontype (sessionid, typename) VALUES (:1, :2)", array($sessionId, $auto_session_type)); - $this->db->pq("UPDATE container SET sessionid=:1, bltimestamp=CURRENT_TIMESTAMP WHERE containerid=:2", array($sessionId, $containerId)); - $this->db->pq("INSERT INTO session_has_person (sessionid, personid, role) VALUES (:1, :2, 'Team Leader')", array($sessionId, $personId)); + $sessionId = $this->db->id(); - $sessionNumber = $sess['resp']->sessionNumber; + $this->db->pq("INSERT INTO sessiontype (sessionid, typename) VALUES (:1, :2)", array($sessionId, $auto_session_type)); + $this->db->pq("UPDATE container SET sessionid=:1, bltimestamp=CURRENT_TIMESTAMP WHERE containerid=:2", array($sessionId, $containerId)); + $this->db->pq("INSERT INTO session_has_person (sessionid, personid, role) VALUES (:1, :2, 'Team Leader')", array($sessionId, $personId)); - } else { - error_log(print_r(array('error' => 'Session could not be created via UAS', 'data' => $data, 'resp' => $sess), True)); - $this->_error('Something went wrong creating a session for that container, response code was: '.$sess['code'].' response: '.json_encode($sess['resp'])); - } + $sessionNumber = $sess['resp']->sessionNumber; } else { - $this->_error("No Samples or investigators! FAILED to create autocollect session!"); + error_log(print_r(array('error' => 'Session could not be created via UAS', 'data' => $data, 'resp' => $sess), True)); + $this->_error('Something went wrong creating a session for that container, response code was: ' . $sess['code'] . ' response: ' . json_encode($sess['resp'])); } - return $sessionNumber; + } else { + $this->_error("No Samples or investigators! FAILED to create autocollect session!"); } - /** - * Given an acitve auto collect session - add this container to the session. - * This function needs to find all the existing samples and investigators so we can update UAS - * - * @param integer $sessionId ISPyB SessionID for currently active Auto Collect session - * @param integer $uasSessionId UAS SessionID for currently active Auto Collect session (from BLSession.externalId) - * @param array $containerId ISPyB ContainerID - * @return Array Result of updating session - null or array with samples/investigators added - */ - function _update_autocollect_session($sessionId, $uasSessionId, $containerId, $teamLeader) { - global $auto_user, $auto_pass; - // Return response if successful - $result = null; - - // First find list of containers in the existing session... - $containers = $this->db->pq("SELECT c.containerId + return $sessionNumber; + } + + /** + * Given an active auto collect session - add this container to the session. + * This function needs to find all the existing samples and investigators so we can update UAS + * + * @param integer $sessionId ISPyB SessionID for currently active Auto Collect session + * @param integer $uasSessionId UAS SessionID for currently active Auto Collect session (from BLSession.externalId) + * @param array $containerId ISPyB ContainerID + * @return Array Result of updating session - null or array with samples/investigators added + */ + function _update_autocollect_session($sessionId, $uasSessionId, $containerId, $teamLeader) + { + global $auto_user, $auto_pass; + // Return response if successful + $result = null; + + // First find list of containers in the existing session... + $containers = $this->db->pq("SELECT c.containerId FROM Container c INNER JOIN BLSession ses ON c.sessionid = ses.sessionid WHERE c.sessionId = :1 ORDER BY c.containerId", array($sessionId)); - if (sizeof($containers)) { - // ...now add the requested container id (the new one) to the list - // Apparently this is better/faster than array_push! - $containers[] = array('CONTAINERID' => $containerId); + if (sizeof($containers)) { + // ...now add the requested container id (the new one) to the list + // Apparently this is better/faster than array_push! + $containers[] = array('CONTAINERID' => $containerId); - // Extract only the parts we want - $containerList = array_map(function($item) { - return strtoupper($item['CONTAINERID']); - }, $containers); + // Extract only the parts we want + $containerList = array_map(function ($item) { + return strtoupper($item['CONTAINERID']); + }, $containers); - $sampleInfo = $this->_get_valid_samples_from_containers($containerList); + $sampleInfo = $this->_get_valid_samples_from_containers($containerList); - // Note specific syntax here that lets us update the sampleInfo['INVESTIGATORS'] array - foreach($sampleInfo['INVESTIGATORS'] as &$investigators) { - if ($investigators['personId'] == $teamLeader) { - $investigators['role'] = 'TEAM_LEADER'; - } + // Note specific syntax here that lets us update the sampleInfo['INVESTIGATORS'] array + foreach ($sampleInfo['INVESTIGATORS'] as &$investigators) { + if ($investigators['personId'] == $teamLeader) { + $investigators['role'] = 'TEAM_LEADER'; } + } - // Patch the UAS session - $data = array( - 'sampleIds' => array_values($sampleInfo['SAMPLES']), - 'investigators' => array_values($sampleInfo['INVESTIGATORS']), + // Patch the UAS session + $data = array( + 'sampleIds' => array_values($sampleInfo['SAMPLES']), + 'investigators' => array_values($sampleInfo['INVESTIGATORS']), + ); + $uas = new UAS($auto_user, $auto_pass); + $code = $uas->update_session($uasSessionId, $data); + + if ($code == 200) { + // Update ISPyB records + $this->db->pq("UPDATE container SET sessionid=:1 WHERE containerid=:2", array($sessionId, $containerId)); + // For debugging - actually just want to return Success! + $result = array( + 'SAMPLES' => array_values($sampleInfo['SAMPLES']), + 'INVESTIGATORS' => array_values($sampleInfo['INVESTIGATORS']), + 'CONTAINERS' => $containerList, ); - $uas = new UAS($auto_user, $auto_pass); - $code = $uas->update_session($uasSessionId, $data); - - if ($code == 200) { - // Update ISPyB records - $this->db->pq("UPDATE container SET sessionid=:1 WHERE containerid=:2", array($sessionId, $containerId)); - // For debugging - actually just want to return Success! - $result = array('SAMPLES' => array_values($sampleInfo['SAMPLES']), - 'INVESTIGATORS' => array_values($sampleInfo['INVESTIGATORS']), - 'CONTAINERS' => $containerList, - ); - } else if ($code == 403) { - $this->_error('UAS Error - samples and/or investigators not valid. ISPyB/UAS Session ID: '. $sessionId . ' / ' . $uasSessionId); - } else if ($code == 404) { - $this->_error('UAS Error - session not found in UAS, Session ID: '. $sessionId . ' UAS Session ID: ' . $uasSessionId); - } else { - $this->_error('UAS Error - something wrong creating a session for that container ' . $containerId . ', response code was: '. $code); - } + } else if ($code == 403) { + $this->_error('UAS Error - samples and/or investigators not valid. ISPyB/UAS Session ID: ' . $sessionId . ' / ' . $uasSessionId); + } else if ($code == 404) { + $this->_error('UAS Error - session not found in UAS, Session ID: ' . $sessionId . ' UAS Session ID: ' . $uasSessionId); } else { - error_log("Something wrong - an Auto Collect session exists but with no containers " . $container['SESSIONID']); - - $this->_error('No valid containers on the existing Auto Collect Session id:', $container['SESSIONID']); + $this->_error('UAS Error - something wrong creating a session for that container ' . $containerId . ', response code was: ' . $code); } - return $result; + } else { + error_log("Something wrong - an Auto Collect session exists but with no containers " . $sessionId); + + $this->_error('No valid containers on the existing Auto Collect Session id:', $sessionId); } - /** - * Find samples and owners of containers in a session - * This function needs to find the samples and investigators for this container - * - * @param array $containers Array of containerIds - * @return Array Returns list of valid samples 'SAMPLES' and investigators 'INVESTIGATORS' (i.e. those with valid UAS references) - */ - function _get_valid_samples_from_containers($containers=array()) { - // If the first (oldest) container is scanned first, then it should be set as the Team Lead - // We have already found relevant container ids by matching session (or it's a new container) - // If the container has an owner then we would not need to link to Proposal via Dewar/Shipping to get an owner - // - // Prepare a string with a list of container ids - to be passed into a SQL query - $containerIds = implode("', '", $containers); - error_log("Getting valid samples/investigators for containers = " . $containerIds); - - $containerResults = $this->db->pq("SELECT c.containerid, HEX(pr.externalid) as externalid, IFNULL(HEX(pe.externalid), HEX(pe2.externalid)) as investigator + return $result; + } + /** + * Find samples and owners of containers in a session + * This function needs to find the samples and investigators for this container + * + * @param array $containers Array of containerIds + * @return Array Returns list of valid samples 'SAMPLES' and investigators 'INVESTIGATORS' (i.e. those with valid UAS references) + */ + function _get_valid_samples_from_containers($containers = array()) + { + // If the first (oldest) container is scanned first, then it should be set as the Team Lead + // We have already found relevant container ids by matching session (or it's a new container) + // If the container has an owner then we would not need to link to Proposal via Dewar/Shipping to get an owner + // + // Prepare a string with a list of container ids - to be passed into a SQL query + $containerIds = implode("', '", $containers); + error_log("Getting valid samples/investigators for containers = " . $containerIds); + + $containerResults = $this->db->pq("SELECT c.containerid, HEX(pr.externalid) as externalid, IFNULL(HEX(pe.externalid), HEX(pe2.externalid)) as investigator FROM Container c INNER JOIN BLSample bls ON bls.containerid = c.containerid INNER JOIN Crystal cr ON cr.crystalid = bls.crystalid @@ -1144,53 +1251,57 @@ function _get_valid_samples_from_containers($containers=array()) { WHERE c.containerId IN ('$containerIds') ORDER BY containerid"); - $samples = array_map(function($result) { - return strtoupper($result['EXTERNALID']); - }, $containerResults); - - $investigators = array_map(function($result) { - return strtoupper($result['INVESTIGATOR']); - }, $containerResults); - - // Strip empty values and provide unique list - $samples = array_filter(array_unique($samples)); - $investigators = array_filter(array_unique($investigators)); - - $uas_investigators = array_map(function($item) { - return array('role' => 'DATA_ACCESS', 'personId' => $item); - }, $investigators); - - return array('SAMPLES' => $samples, 'INVESTIGATORS' => $uas_investigators); - } - - - # ------------------------------------------------------------------------ - # Close visit for auto-collect - function _close_auto_visit() { - $this->_perform_visit_action(); - } - - # ------------------------------------------------------------------------ - # Update Session with used time report for auto-collect in UAS and update session end date on Ispyb - function _update_auto_visit() { - $session_update_data = array(); - if ($this->has_arg('Total Time')) { - $session_update_data['usedTimeReport'] = array( - 'log' => array( - 'Total Time' => $this->arg('Total Time') - ) - ); - } - $this->_perform_visit_action($session_update_data); + $samples = array_map(function ($result) { + return strtoupper($result['EXTERNALID']); + }, $containerResults); + + $investigators = array_map(function ($result) { + return strtoupper($result['INVESTIGATOR']); + }, $containerResults); + + // Strip empty values and provide unique list + $samples = array_filter(array_unique($samples)); + $investigators = array_filter(array_unique($investigators)); + + $uas_investigators = array_map(function ($item) { + return array('role' => 'DATA_ACCESS', 'personId' => $item); + }, $investigators); + + return array('SAMPLES' => $samples, 'INVESTIGATORS' => $uas_investigators); + } + + # ------------------------------------------------------------------------ + # Close visit for auto-collect + function _close_auto_visit() + { + $this->_perform_visit_action(); + } + + # ------------------------------------------------------------------------ + # Update Session with used time report for auto-collect in UAS and update session end date on Ispyb + function _update_auto_visit() + { + $session_update_data = array(); + if ($this->has_arg('Total Time')) { + $session_update_data['usedTimeReport'] = array( + 'log' => array( + 'Total Time' => $this->arg('Total Time') + ) + ); } + $this->_perform_visit_action($session_update_data); + } - function _perform_visit_action($close_session_data = array()) { - global $auto, $auto_bls, $auto_user, $auto_pass; + function _perform_visit_action($close_session_data = array()) + { + global $auto, $auto_bls, $auto_user, $auto_pass; - if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) $this->_error('You do not have access to that resource'); - if (!$this->has_arg('CONTAINERID')) $this->_error('No containerid specified'); + if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) + $this->_error('You do not have access to that resource'); + if (!$this->has_arg('CONTAINERID')) + $this->_error('No containerid specified'); - $cont = $this->db->pq("SELECT c.sessionid, c.containerid, HEX(ses.externalid) as externalid, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit + $cont = $this->db->pq("SELECT c.sessionid, c.containerid, HEX(ses.externalid) as externalid, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit FROM proposal p INNER JOIN shipping s ON s.proposalid = p.proposalid INNER JOIN dewar d ON d.shippingid = s.shippingid @@ -1198,26 +1309,25 @@ function _perform_visit_action($close_session_data = array()) { LEFT OUTER JOIN blsession ses ON c.sessionid = ses.sessionid WHERE c.containerid=:1", array($this->arg('CONTAINERID'))); - if (!sizeof($cont)) $this->_error('No such container'); - $cont = $cont[0]; - - if ($cont['SESSIONID']) { - $uas = new UAS($auto_user, $auto_pass); - $code = $uas->close_session($cont['EXTERNALID'], $close_session_data); + if (!sizeof($cont)) + $this->_error('No such container'); + $cont = $cont[0]; - if ($code == 200) { - // Don't wait for UAS sync - set end Date in ISPyB now as well - $this->db->pq("UPDATE blsession SET endDate=CURRENT_TIMESTAMP WHERE sessionid=:1", array($cont['SESSIONID'])); - $this->_output(array('MESSAGE' => 'Session closed', 'VISIT' => $cont['VISIT'])); - } else if ($code == 403) { - $this->_output(array('MESSAGE' => 'Session already closed', 'VISIT' => $cont['VISIT'])); - - } else { - $this->_error('Something went wrong closing that session, response code was: '.$code); - } + if ($cont['SESSIONID']) { + $uas = new UAS($auto_user, $auto_pass); + $code = $uas->close_session($cont['EXTERNALID'], $close_session_data); + if ($code == 200) { + // Don't wait for UAS sync - set end Date in ISPyB now as well + $this->db->pq("UPDATE blsession SET endDate=CURRENT_TIMESTAMP WHERE sessionid=:1", array($cont['SESSIONID'])); + $this->_output(array('MESSAGE' => 'Session closed', 'VISIT' => $cont['VISIT'])); + } else if ($code == 403) { + $this->_output(array('MESSAGE' => 'Session already closed', 'VISIT' => $cont['VISIT'])); } else { - $this->_error('That container does not have a session'); + $this->_error('Something went wrong closing that session, response code was: ' . $code); } + } else { + $this->_error('That container does not have a session'); } + } } diff --git a/api/src/Page/Robot.php b/api/src/Page/Robot.php index 68ba95b1f..da209ff59 100644 --- a/api/src/Page/Robot.php +++ b/api/src/Page/Robot.php @@ -8,7 +8,7 @@ class Robot extends Page { - public static $arg_list = array('bl' => '[\w-]+', + public static $arg_list = array('bl' => '[\w\-]+', 'run' => '\d+', 'visit' => '\w+\d+-\d+', 'year' => '\d\d\d\d', @@ -35,14 +35,14 @@ function _averages() { : "MEDIAN(TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE))) as avgt"; - $rows = $this->db->pq("SELECT CONCAT(CONCAT(vr.run, '-'), s.beamlinename) as rbl, min(vr.run) as run, min(vr.runid) as runid, min(s.beamlinename) as bl, count(r.robotactionid) as num, + $rows = $this->db->pq("SELECT CONCAT(vr.run, '-', s.beamlinename) as rbl, min(vr.run) as run, min(vr.runid) as runid, min(s.beamlinename) as bl, count(r.robotactionid) as num, $median FROM v_run vr INNER JOIN blsession s ON (s.startdate BETWEEN vr.startdate AND vr.enddate) INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN robotaction r ON (r.blsessionid = s.sessionid) WHERE /*r.robotactionid > 1 AND*/ p.proposalcode <> 'cm' AND r.status='SUCCESS' AND (r.actiontype = 'LOAD') - GROUP BY CONCAT(CONCAT(vr.run, '-'), s.beamlinename) + GROUP BY CONCAT(vr.run, '-', s.beamlinename) ORDER BY min(s.beamlinename), min(vr.runid)"); $tvs = $this->db->pq("SELECT distinct vr.run,vr.runid @@ -122,7 +122,7 @@ function _errors() { array_push($args, $this->arg('run')); } if ($this->has_arg('visit')) { - array_push($where, "CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :" . (sizeof($args)+1)); + array_push($where, "CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :" . (sizeof($args)+1)); array_push($args, $this->arg('visit')); } if ($this->has_arg('s')) { @@ -154,7 +154,7 @@ function _errors() { array_push($args, $start); array_push($args, $end); - $errors = $this->db->paginate("SELECT r.samplebarcode, r.actiontype, r.dewarlocation, r.containerlocation, r.message, TO_CHAR(r.starttimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as vis, s.beamlinename as bl, r.status, TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)) as time + $errors = $this->db->paginate("SELECT r.samplebarcode, r.actiontype, r.dewarlocation, r.containerlocation, r.message, TO_CHAR(r.starttimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis, s.beamlinename as bl, r.status, TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)) as time FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate @@ -188,7 +188,7 @@ function _visit_profile() { } if ($this->has_arg('visit')) { - $where .= " AND CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber), '-'), s.visit_number) LIKE :".(sizeof($args)+1); + $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :".(sizeof($args)+1); array_push($args, $this->arg('visit')); } @@ -266,7 +266,7 @@ function _totals() { INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN robotaction r ON (r.blsessionid = s.sessionid) WHERE p.proposalcode <> 'cm' AND $where AND (r.actiontype = 'LOAD') - GROUP BY CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number)) inq", $args); + GROUP BY CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number)) inq", $args); $tot = intval($tot[0]['TOT']); $start = 0; @@ -289,13 +289,13 @@ function _totals() { "ROUND(AVG(CASE WHEN r.status='SUCCESS' then TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)) END),1) as avgt" : "ROUND(MEDIAN(CASE WHEN r.status='SUCCESS' then TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)) END),1) as avgt"; - $q = "SELECT TO_CHAR(min(r.starttimestamp), 'DD-MM-YYYY HH24:MI:SS') as st, CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber), '-'), s.visit_number) as vis, s.beamlinename as bl, count(r.robotactionid) as num, count(CASE WHEN r.status='SUCCESS' then 1 end) as success, count(CASE WHEN r.status='ERROR' then 1 end) as error, count(CASE WHEN r.status='CRITICAL' then 1 end) as critical, count(CASE WHEN r.status='WARNING' then 1 end) as warning, count(CASE WHEN r.status='EPICSFAIL' then 1 end) as epicsfail, count(CASE WHEN r.status='COMMANDNOTSENT' then 1 end) as commandnotsent, + $q = "SELECT TO_CHAR(min(r.starttimestamp), 'DD-MM-YYYY HH24:MI:SS') as st, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as vis, s.beamlinename as bl, count(r.robotactionid) as num, count(CASE WHEN r.status='SUCCESS' then 1 end) as success, count(CASE WHEN r.status='ERROR' then 1 end) as error, count(CASE WHEN r.status='CRITICAL' then 1 end) as critical, count(CASE WHEN r.status='WARNING' then 1 end) as warning, count(CASE WHEN r.status='EPICSFAIL' then 1 end) as epicsfail, count(CASE WHEN r.status='COMMANDNOTSENT' then 1 end) as commandnotsent, $median FROM v_run vr INNER JOIN blsession s ON (s.startdate BETWEEN vr.startdate AND vr.enddate) INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN robotaction r ON (r.blsessionid = s.sessionid) WHERE p.proposalcode <> 'cm' AND $where AND r.actiontype = 'LOAD' - GROUP BY CONCAT(CONCAT(CONCAT(p.proposalcode,p.proposalnumber), '-'), s.visit_number), s.beamlinename + GROUP BY CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number), s.beamlinename ORDER BY min(r.starttimestamp) DESC"; $rows = $this->db->paginate($q, $args); diff --git a/api/src/Page/Sample.php b/api/src/Page/Sample.php index 663f617ff..b9f4bce38 100644 --- a/api/src/Page/Sample.php +++ b/api/src/Page/Sample.php @@ -7,494 +7,525 @@ class Sample extends Page { - - - public static $arg_list = array('page' => '\d+', - 'per_page' => '\d+', - 'sort_by' => '\w+', - 'order' => '\w+', - 's' => '\w+', - - - 'prop' => '\w+\d+', - 'term' => '\w+', - 'pid' => '\d+', - 'sid' => '\d+', - 'ssid' => '\d+', - 'cid' => '\d+', - 'crid' => '\d+', - 'lid' => '\d+', - 'value' => '.*', - 'ty' => '\w+', - 't' => '\w+', - 'pjid' => '\d+', - 'imp' => '\d', - 'lt' => '\w+', - 'existing_pdb' => '\d+', - 'pdb_code' => '\w\w\w\w', - 'pdbid' => '\d+', - 'visit' => '\w+\d+-\d+', - 'type' => '\d+', - 'global' => '\d+', - 'seq' => '\d', - 'bcd' => '\d', - 'phase' => '', - 'crystal' => '', - 'container' => '', - 'capillary' => '', - 'capillaryPhase' => '', - 'json' => '', - 'dcp' => '\d', - - 'collected_during' => '\w+\d+-\d+', - - 'DEWARID' => '\d+', - 'PROTEINID' => '\d+', - 'CRYSTALID' => '\d+', - 'CONTAINERID' => '\d+', - 'LOCATION' => '\d+', - 'CODE' => '(\w|\s|\-)+|^$', // Change validation to work for dashes as well as numbers - // 'NAME' => '.*', - 'ACRONYM' => '([\w-])+', - 'SEQUENCE' => '[\s\w\(\)\.>\|;\n]+', - 'MOLECULARMASS' => '\d+(.\d+)?', - 'VOLUME' => '\d+(.\d+)?', - 'DENSITY' => '\d+(.\d+)?', - 'THEORETICALDENSITY' => '\d+(.\d+)?', - - 'NAME' => '[\w\s-()]+', - 'COMMENTS' => '.*', - 'SPACEGROUP' => '(\w|\s|\-|\/)+|^$', // Any word character (inc spaces bars and slashes) or empty string - 'CELL_A' => '\d+(.\d+)?', - 'CELL_B' => '\d+(.\d+)?', - 'CELL_C' => '\d+(.\d+)?', - 'CELL_ALPHA' => '\d+(.\d+)?', - 'CELL_BETA' => '\d+(.\d+)?', - 'CELL_GAMMA' => '\d+(.\d+)?', - 'REQUIREDRESOLUTION' => '\d+(.\d+)?', - 'ANOMALOUSSCATTERER' => '(\w+)|^$', // Any word character or empty string - 'BLSUBSAMPLEID' => '\d+', - 'SCREENCOMPONENTGROUPID' => '\d+', - - 'COMPONENTTYPEID' => '\d+', - 'CONCENTRATIONTYPEID' => '\d+', - 'GLOBAL' => '\d+', - - 'COMPONENTIDS' => '\d+', - 'COMPONENTAMOUNTS' => '\d+(.\d+)?', - 'ABUNDANCE' => '\d+(.\d+)?', - 'PACKINGFRACTION' => '\d+(.\d+)?', - 'DIMENSION1' => '\d+(.\d+)?', - 'DIMENSION2' => '\d+(.\d+)?', - 'DIMENSION3' => '\d+(.\d+)?', - 'SHAPE' => '\w+', - 'LOOPTYPE' => '\w+', - 'COMPONENTID' => '\d+', - 'BLSAMPLETYPEID' => '\d+', - 'scid' => '\d+-\d+', - - 'BLSAMPLEID' => '\d+', - 'X' => '\d+(.\d+)?', - 'Y' => '\d+(.\d+)?', - 'Z' => '\d+(.\d+)?', - 'X2' => '\d+(.\d+)?', - 'Y2' => '\d+(.\d+)?', - 'Z2' => '\d+(.\d+)?', - - 'EXPERIMENTKIND' => '[\w|\s]+', - 'CENTRINGMETHOD' => '\w+', - 'RADIATIONSENSITIVITY' => '\w+', - 'ENERGY' => '\w+', - 'USERPATH' => '(?=.{0,40}$)(\w|-)+\/?(\w|-)+', // Up to two folders as a path, 40 characters maximum - 'EXPOSURETIME' => '\d+(.\d+)?', - 'PREFERREDBEAMSIZEX' => '\d+(.\d+)?', - 'PREFERREDBEAMSIZEY' => '\d+(.\d+)?', - 'BOXSIZEX' => '\d+', - 'BOXSIZEY' => '\d+', - 'NUMBEROFIMAGES' => '\d+', - 'AXISSTART' => '-?\d+(.\d+)?', - 'AXISRANGE' => '\d+(.\d+)?', - 'TRANSMISSION' => '\d+(.\d+)?', - 'ENERGY' => '\d+(.\d+)?', - 'MONOCHROMATOR' => '\w+', - 'PRESET' => '\d', - 'BEAMLINENAME' => '[\w-]+', - - 'queued' => '\d', - 'UNQUEUE' => '\d', - 'nodata' => '\d', - 'notcompleted' => '\d', - - // external is a flag to indicate this protein/sample has a user office system id - // whereas externalid is the actual reference - 'external' => '\d', - // Original refers to the real user office sample - not any clone / copy - 'original' => '\d', - 'EXTERNALID' => '\w+', - 'SAFETYLEVEL' => '\w+', - - 'COMPONENTLATTICEID' => '\d+', - - 'BLSAMPLEGROUPID' => '\d+', - 'GROUPORDER' => '\d+', - 'TYPE' => '\w+', - 'BLSAMPLEGROUPSAMPLEID' => '\d+-\d+', - 'PLANORDER' => '\d', - - 'SAMPLEGROUPID' => '\d+', - 'SCREENINGMETHOD' => '\w+', - 'SCREENINGCOLLECTVALUE' => '\d+', - 'SAMPLEGROUP' => '\d+', - 'INITIALSAMPLEGROUP' => '\d+', - 'STRATEGYOPTION' => '', - 'MINIMUMRESOLUTION' => '\d+(.\d+)?', - 'groupSamplesType' => '.*' // query parameter to query sample groups by sample types. Should be comma separated values like so: groupSamplesType=container,capillary - ); - - - public static $dispatch = array(array('(/:sid)(/cid/:cid)', 'get', '_samples'), - array('', 'put', '_bulk_update_sample_full'), - array('/:sid', 'patch', '_update_sample'), - array('/:sid', 'put', '_update_sample_full'), - array('', 'post', '_add_sample'), - array('/simple', 'post', '_add_simple_sample'), - - array('/components', 'post', '_add_sample_component'), - array('/components/:scid', 'delete', '_remove_sample_component'), - - array('/sub(/sid/:sid)', 'get', '_sub_samples'), - array('/sub/:ssid', 'get', '_get_sub_sample'), - array('/sub/:ssid', 'patch', '_update_sub_sample'), - array('/sub/:ssid', 'put', '_update_sub_sample_full'), - array('/sub', 'post', '_add_sub_sample'), - array('/sub/:ssid', 'delete', '_delete_sub_sample'), - array('/sub/queue(/:BLSUBSAMPLEID)', 'get', '_pre_q_sub_sample'), - - array('/plan', 'get', '_get_diffraction_plans'), - array('/plan', 'post', '_add_diffraction_plan'), - array('/plan/:pid', 'patch', '_update_diffraction_plan'), - array('/plan/:pid', 'delete', '_delete_diffraction_plan'), - - - array('/proteins(/:pid)', 'get', '_proteins'), - array('/proteins', 'post', '_add_protein'), - array('/proteins/:pid', 'patch', '_update_protein'), - array('/proteins/distinct', 'get', '_disinct_proteins'), - - array('/proteins/lattice(/:lid)', 'get', '_protein_lattices'), - array('/proteins/lattice', 'post', '_add_protein_lattice'), - array('/proteins/lattice/:lid', 'patch', '_update_protein_lattice'), - - - array('/crystals(/:CRYSTALID)', 'get', '_crystals'), - array('/crystals', 'post', '_add_crystal'), - array('/crystals/:CRYSTALID', 'patch', '_update_crystal'), - - array('/pdbs(/pid/:pid)', 'get', '_get_pdbs'), - array('/pdbs', 'post', '_add_pdb'), - array('/pdbs(/:pdbid)', 'delete', '_remove_pdb'), - - array('/concentrationtypes', 'get', '_concentration_types'), - array('/componenttypes', 'get', '_component_types'), - - array('/groups', 'get', '_sample_groups'), - array('/groups', 'post', '_add_new_sample_group'), - array('/groups/:BLSAMPLEID', 'get', '_get_sample_groups_by_sample'), - array('/groups/:BLSAMPLEGROUPID/samples', 'post', '_add_sample_to_group'), - array('/groups/:BLSAMPLEGROUPID/samples/:BLSAMPLEGROUPSAMPLEID', 'put', '_update_sample_in_group'), - array('/groups/:BLSAMPLEGROUPID/samples/:BLSAMPLEGROUPSAMPLEID', 'delete', '_remove_sample_from_group'), - array('/groups/:BLSAMPLEGROUPID/samples', 'get', '_get_sample_group_samples'), - array('/spacegroups(/:SPACEGROUPID)', 'get', '_get_spacegroups'), - - ); + public static $arg_list = array( + 'page' => '\d+', + 'per_page' => '\d+', + 'sort_by' => '\w+', + 'order' => '\w+', + 's' => '\w+', + 'prop' => '\w+\d+', + 'term' => '\w+', + 'pid' => '\d+', + 'sid' => '\d+', + 'ssid' => '\d+', + 'cid' => '\d+', + 'crid' => '\d+', + 'lid' => '\d+', + 'value' => '.*', + 'ty' => '\w+', + 't' => '\w+', + 'pjid' => '\d+', + 'imp' => '\d', + 'lt' => '\w+', + 'existing_pdb' => '\d+', + 'pdb_code' => '\w\w\w\w', + 'pdbid' => '\d+', + 'visit' => '\w+\d+-\d+', + 'type' => '\d+', + 'global' => '\d+', + 'seq' => '\d', + 'bcd' => '\d', + 'phase' => '', + 'crystal' => '', + 'container' => '', + 'capillary' => '', + 'capillaryPhase' => '', + 'json' => '', + 'dcp' => '\d', + + 'collected_during' => '\w+\d+-\d+', + + 'DEWARID' => '\d+', + 'PROTEINID' => '\d+', + 'CRYSTALID' => '\d+', + 'CONTAINERID' => '\d+', + 'LOCATION' => '\d+', + 'CODE' => '(\w|\s|\-)+|^$', // Change validation to work for dashes as well as numbers + 'ACRONYM' => '([\w\-])+', + 'SEQUENCE' => '[\s\w\(\)\.>\|;\n]+', + 'MOLECULARMASS' => '\d+(.\d+)?', + 'VOLUME' => '\d+(.\d+)?', + 'DENSITY' => '\d+(.\d+)?', + 'THEORETICALDENSITY' => '\d+(.\d+)?', + + 'NAME' => '^[a-zA-Z0-9\-_\.]+$', + 'COMMENTS' => '.*', + 'SPACEGROUP' => '(\w|\s|\-|\/)+|^$', // Any word character (inc spaces bars and slashes) or empty string + 'CELL_A' => '\d+(.\d+)?', + 'CELL_B' => '\d+(.\d+)?', + 'CELL_C' => '\d+(.\d+)?', + 'CELL_ALPHA' => '\d+(.\d+)?', + 'CELL_BETA' => '\d+(.\d+)?', + 'CELL_GAMMA' => '\d+(.\d+)?', + 'REQUIREDRESOLUTION' => '\d+(.\d+)?', + 'ANOMALOUSSCATTERER' => '(\w+)|^$', // Any word character or empty string + 'BLSUBSAMPLEID' => '\d+', + 'SCREENCOMPONENTGROUPID' => '\d+', + + 'COMPONENTTYPEID' => '\d+', + 'CONCENTRATIONTYPEID' => '\d+', + 'GLOBAL' => '\d+', + + 'COMPONENTIDS' => '\d+', + 'COMPONENTAMOUNTS' => '\d+(.\d+)?', + 'ABUNDANCE' => '\d+(.\d+)?', + 'PACKINGFRACTION' => '\d+(.\d+)?', + 'DIMENSION1' => '\d+(.\d+)?', + 'DIMENSION2' => '\d+(.\d+)?', + 'DIMENSION3' => '\d+(.\d+)?', + 'SHAPE' => '\w+', + 'COLOR' => '\w+', + 'LOOPTYPE' => '\w+', + 'COMPONENTID' => '\d+', + 'BLSAMPLETYPEID' => '\d+', + 'scid' => '\d+-\d+', + + 'BLSAMPLEID' => '\d+', + 'X' => '\d+(.\d+)?', + 'Y' => '\d+(.\d+)?', + 'Z' => '\d+(.\d+)?', + 'X2' => '\d+(.\d+)?', + 'Y2' => '\d+(.\d+)?', + 'Z2' => '\d+(.\d+)?', + + 'EXPERIMENTKIND' => '[\w|\s]+', + 'CENTRINGMETHOD' => '\w+', + 'RADIATIONSENSITIVITY' => '\w+', + 'USERPATH' => '(?=.{0,40}$)(\w|-)+\/?(\w|-)+', // Up to two folders as a path, 40 characters maximum + 'EXPOSURETIME' => '\d+(.\d+)?', + 'PREFERREDBEAMSIZEX' => '\d+(.\d+)?', + 'PREFERREDBEAMSIZEY' => '\d+(.\d+)?', + 'BOXSIZEX' => '\d+', + 'BOXSIZEY' => '\d+', + 'NUMBEROFIMAGES' => '\d+', + 'AXISSTART' => '-?\d+(.\d+)?', + 'AXISRANGE' => '\d+(.\d+)?', + 'TRANSMISSION' => '\d+(.\d+)?', + 'ENERGY' => '\d+(.\d+)?', + 'MONOCHROMATOR' => '\w+', + 'PRESET' => '\d', + 'BEAMLINENAME' => '[\w\-]+', + + 'queued' => '\d', + 'UNQUEUE' => '\d', + 'nodata' => '\d', + 'notcompleted' => '\d', + + // external is a flag to indicate this protein/sample has a user office system id + // whereas externalid is the actual reference + 'external' => '\d', + // Original refers to the real user office sample - not any clone / copy + 'original' => '\d', + 'EXTERNALID' => '\w+', + 'SAFETYLEVEL' => '\w+', + + 'COMPONENTLATTICEID' => '\d+', + + 'BLSAMPLEGROUPID' => '\d+', + 'GROUPORDER' => '\d+', + 'TYPE' => '\w+', + 'BLSAMPLEGROUPSAMPLEID' => '\d+-\d+', + 'PLANORDER' => '\d', + + 'SAMPLEGROUPID' => '\d+', + 'SCREENINGMETHOD' => '\w+', + 'SCREENINGCOLLECTVALUE' => '\d+', + 'SAMPLEGROUP' => '\d+', + 'INITIALSAMPLEGROUP' => '\d+', + 'STRATEGYOPTION' => '', + 'MINIMUMRESOLUTION' => '\d+(.\d+)?', + 'groupSamplesType' => '.*' // query parameter to query sample groups by sample types. Should be comma separated values like so: groupSamplesType=container,capillary + ); + + + public static $dispatch = array( + array('(/:sid)(/cid/:cid)', 'get', '_samples'), + array('', 'put', '_bulk_update_sample_full'), + array('/:sid', 'patch', '_update_sample'), + array('/:sid', 'put', '_update_sample_full'), + array('', 'post', '_add_sample'), + array('/simple', 'post', '_add_simple_sample'), + array('/move/:sid', 'post', '_move_sample_to_another_container'), // Should be a PATCH request but for some weird reason the request body for PATCH requests converts all integers to string, and then it fails validation. + + array('/components', 'post', '_add_sample_component'), + array('/components/:scid', 'delete', '_remove_sample_component'), + + array('/sub(/sid/:sid)', 'get', '_sub_samples'), + array('/sub/:ssid', 'get', '_get_sub_sample'), + array('/sub/:ssid', 'patch', '_update_sub_sample'), + array('/sub/:ssid', 'put', '_update_sub_sample_full'), + array('/sub', 'post', '_add_sub_sample'), + array('/sub/:ssid', 'delete', '_delete_sub_sample'), + array('/sub/queue/cid/:cid', 'post', '_queue_all_sub_samples'), + array('/sub/queue(/:BLSUBSAMPLEID)', 'get', '_pre_q_sub_sample'), + + array('/plan', 'get', '_get_diffraction_plans'), + array('/plan', 'post', '_add_diffraction_plan'), + array('/plan/:pid', 'patch', '_update_diffraction_plan'), + array('/plan/:pid', 'delete', '_delete_diffraction_plan'), + + array('/proteins(/:pid)', 'get', '_proteins'), + array('/proteins', 'post', '_add_protein'), + array('/proteins/:pid', 'patch', '_update_protein'), + array('/proteins/distinct', 'get', '_disinct_proteins'), + + array('/proteins/lattice(/:lid)', 'get', '_protein_lattices'), + array('/proteins/lattice', 'post', '_add_protein_lattice'), + array('/proteins/lattice/:lid', 'patch', '_update_protein_lattice'), + + array('/crystals(/:CRYSTALID)', 'get', '_crystals'), + array('/crystals', 'post', '_add_crystal'), + array('/crystals/:CRYSTALID', 'patch', '_update_crystal'), + + array('/pdbs(/pid/:pid)', 'get', '_get_pdbs'), + array('/pdbs', 'post', '_add_pdb'), + array('/pdbs(/:pdbid)', 'delete', '_remove_pdb'), + + array('/concentrationtypes', 'get', '_concentration_types'), + array('/componenttypes', 'get', '_component_types'), + + array('/groups', 'get', '_sample_groups'), + array('/groups', 'post', '_add_new_sample_group'), + array('/groups/:BLSAMPLEID', 'get', '_get_sample_groups_by_sample'), + + array('/groups/:BLSAMPLEGROUPID', 'get', '_get_sample_group'), + array('/groups/:BLSAMPLEGROUPID', 'patch', '_update_sample_group'), + array('/groups/:BLSAMPLEGROUPID/samples', 'get', '_get_sample_group_samples'), + array('/groups/:BLSAMPLEGROUPID/samples', 'post', '_add_samples_to_group'), + array('/groups/:BLSAMPLEGROUPID/samples/:BLSAMPLEGROUPSAMPLEID', 'put', '_update_sample_in_group'), + array('/groups/:BLSAMPLEGROUPID/samples/:BLSAMPLEGROUPSAMPLEID', 'delete', '_remove_sample_from_group'), + array('/groups/containers/all', 'get', '_get_sample_group_samples_by_container'), + + array('/spacegroups(/:SPACEGROUPID)', 'get', '_get_spacegroups'), + + ); + + + /** + * Insert Protein, Crystal, Container, BLSample, DiffractionPlan (+associated tables) and PDB file information as a single transaction + * Can add one or more complete sets of sample information to ISPyB from a form submission or a file upload + * */ + function _add_simple_sample() + { + // ID holder for all the ids associated with the current iteration + // This will be populated by getting ids of each entity after insertion + // Mostly a helper store for ids but also the return value of this method + $ids = array(); + + // Placeholder capillary id variables that will be null until populated (if required) on first iteration of foreach loop below + // For further iterations the value assigned will be re-used to associated remaining samples with the set capillary + $capillaryPhaseId = null; + $capillaryId = null; + $blSampleCapillaryId = null; + + // Default values used for diffraction plan + $defaultBeamSizeX = 70; + $defaultBeamSizeY = 70; + $defaultEnergy = 76600; + $defaultMonoBandwidth = 0.1; + + // Hardcoded detector ids? + $detector1_id = 28; + $detector1_distance = 200; + + $detector2_id = 25; + $detector2_distance = 800; + + $this->db->start_transaction(); /** - * Insert Protein, Crystal, Container, BLSample, DiffractionPlan (+associated tables) and PDB file information as a single transaction - * Can add one or more complete sets of sample information to ISPyB from a form submission or a file upload + * Sets of sample information will appear in $args as "sample_1", "sample_2" etc + * Therefore we can reuse the code for form submission or file upload * */ - function _add_simple_sample() { - // ID holder for all the ids associated with the current iteration - // This will be populated by getting ids of each entity after insertion - // Mostly a helper store for ids but also the return value of this method - $ids = array(); - - // Placeholder capillary id variables that will be null until populated (if required) on first iteration of foreach loop below - // For further iterations the value assigned will be re-used to associated remaining samples with the set capillary - $capillaryPhaseId = null; - $capillaryId = null; - $blSampleCapillaryId = null; - - // Default values used for diffraction plan - $defaultBeamSizeX = 70; - $defaultBeamSizeY = 70; - $defaultEnergy = 76600; - $defaultMonoBandwidth = 0.1; - - // Hardcoded detector ids? - $detector1_id = 28; - $detector1_distance = 200; - - $detector2_id = 25; - $detector2_distance = 800; - - $this->db->start_transaction(); - - /** - * Sets of sample information will appear in $args as "sample_1", "sample_2" etc - * Therefore we can reuse the code for form submission or file upload - * */ - foreach($this->args as $model => $attrs) { - // Not interested in anything that isn't sample information - if(substr($model, 0, strpos($model, '_', 0)) != 'sample') { - continue; - } else { - // Critical model validation (we won't insert anything if these fail) - if (!$attrs->prop) $this->_error('No proposal specified'); - if (!$attrs->crystal) $this->_error('No crystal defined'); - if (!$attrs->capillary) $this->_error('No capillary defined'); - if (!$attrs->capillaryPhase) $this->_error('No capillary material defined'); - if (!$attrs->phase) $this->_error('No phase defined'); - if (!$attrs->container) $this->_error('No container defined'); - if (!$attrs->DEWARID) $this->_error('No default dewar defined'); - - $phase = $attrs->phase; - $crystal = $attrs->crystal; - $container = $attrs->container; - $capillary = $attrs->capillary; - $capillaryPhase = $attrs->capillaryPhase; - - // Critical sub model validation, we again can't proceed if anything is missing - if (!array_key_exists('PROTEINID', $phase) && !array_key_exists('ACRONYM', $phase)) $this->_error('No protein id or acronym'); - if (!array_key_exists('ACRONYM', $capillaryPhase)) $this->_error('No protein acronym for capillary material'); - if (!array_key_exists('NAME', $crystal)) $this->_error('No crystal name specified'); - if (!array_key_exists('NAME', $capillary) || !array_key_exists('CRYSTALID', $capillary)) $this->_error('No capillary name specified'); - if (!array_key_exists('NAME', $container)) $this->_error('No container name specified'); - if (!array_key_exists('CONTAINERTYPE', $container)) $this->_error('No container type specified'); - - // Create ID holder for this iteration (this set of sample information) - $ids[$model] = array(); - - /** - * Insert Proteins - * For each iteration there will be an actual protein and usually (not always) a capillary in which the protein is held - * A Protein can be 'containerless' (no capillary) as identified by a boolean flag, or it may be associated with an existing capillary already in ISPyB - * We need to check the above conditions to see if we are being asked to create a new capillary, re-use an existing one or do nothing (containerless) - * If we need to add protein information for a capillary it will be added to an array with the actual protein information - * - * When a file upload is received, it's only possible to associate all the samples with one capillary (whether it already existed or is to be created) - * The variable $capillaryPhaseId will be null on first iteration which will ensure a new capillary is added and then re-used for all other iterations - */ - $phases = null; - if($attrs->fromFile) - $phases = $capillaryPhaseId == null && !$capillary->CONTAINERLESS && $capillary->CRYSTALID == null ? array($capillaryPhase, $phase) : array($phase); - else - $phases = $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS ? array($capillaryPhase, $phase) : array($phase); - - $isCapillary = sizeof($phases) > 1 ? true : false; - - foreach($phases as $protein){ - $phaseName = array_key_exists('NAME', $protein) ? $protein->NAME : ''; - $phaseSeq = array_key_exists('SEQUENCE', $protein) ? $protein->SEQUENCE : ''; - $phaseMass = array_key_exists('MOLECULARMASS', $protein) ? $protein->MOLECULARMASS : null; - $phaseDensity = array_key_exists('DENSITY', $protein) ? $protein->DENSITY : null; - $externalid = array_key_exists('EXTERNALID', $protein) ? $protein->EXTERNALID : null; - - $chk = $this->db->pq("SELECT proteinid FROM protein + foreach ($this->args as $model => $attrs) { + // Not interested in anything that isn't sample information + if (substr($model, 0, strpos($model, '_', 0)) != 'sample') { + continue; + } else { + // Critical model validation (we won't insert anything if these fail) + if (!$attrs->prop) + $this->_error('No proposal specified'); + if (!$attrs->crystal) + $this->_error('No crystal defined'); + if (!$attrs->capillary) + $this->_error('No capillary defined'); + if (!$attrs->capillaryPhase) + $this->_error('No capillary material defined'); + if (!$attrs->phase) + $this->_error('No phase defined'); + if (!$attrs->container) + $this->_error('No container defined'); + if (!$attrs->DEWARID) + $this->_error('No default dewar defined'); + + $phase = $attrs->phase; + $crystal = $attrs->crystal; + $container = $attrs->container; + $capillary = $attrs->capillary; + $capillaryPhase = $attrs->capillaryPhase; + + // Critical sub model validation, we again can't proceed if anything is missing + if (!array_key_exists('PROTEINID', $phase) && !array_key_exists('ACRONYM', $phase)) + $this->_error('No protein id or acronym'); + if (!array_key_exists('ACRONYM', $capillaryPhase)) + $this->_error('No protein acronym for capillary material'); + if (!array_key_exists('NAME', $crystal)) + $this->_error('No crystal name specified'); + if (!array_key_exists('NAME', $capillary) || !array_key_exists('CRYSTALID', $capillary)) + $this->_error('No capillary name specified'); + if (!array_key_exists('NAME', $container)) + $this->_error('No container name specified'); + if (!array_key_exists('CONTAINERTYPE', $container)) + $this->_error('No container type specified'); + + // Create ID holder for this iteration (this set of sample information) + $ids[$model] = array(); + + /** + * Insert Proteins + * For each iteration there will be an actual protein and usually (not always) a capillary in which the protein is held + * A Protein can be 'containerless' (no capillary) as identified by a boolean flag, or it may be associated with an existing capillary already in ISPyB + * We need to check the above conditions to see if we are being asked to create a new capillary, re-use an existing one or do nothing (containerless) + * If we need to add protein information for a capillary it will be added to an array with the actual protein information + * + * When a file upload is received, it's only possible to associate all the samples with one capillary (whether it already existed or is to be created) + * The variable $capillaryPhaseId will be null on first iteration which will ensure a new capillary is added and then re-used for all other iterations + */ + $phases = null; + if ($attrs->fromFile) + $phases = $capillaryPhaseId == null && !$capillary->CONTAINERLESS && $capillary->CRYSTALID == null ? array($capillaryPhase, $phase) : array($phase); + else + $phases = $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS ? array($capillaryPhase, $phase) : array($phase); + + $isCapillary = sizeof($phases) > 1 ? true : false; + + foreach ($phases as $protein) { + $phaseName = array_key_exists('NAME', $protein) ? $protein->NAME : ''; + $phaseSeq = array_key_exists('SEQUENCE', $protein) ? $protein->SEQUENCE : ''; + $phaseMass = array_key_exists('MOLECULARMASS', $protein) ? $protein->MOLECULARMASS : null; + $phaseDensity = array_key_exists('DENSITY', $protein) ? $protein->DENSITY : null; + $externalid = array_key_exists('EXTERNALID', $protein) ? $protein->EXTERNALID : null; + + $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proposalid=:1 AND acronym=:2", array($this->proposalid, $protein->ACRONYM)); - if (sizeof($chk)) $this->_error('Protein acronym ' . $protein->ACRONYM . ' already exists in this proposal'); + if (sizeof($chk)) + $this->_error('Protein acronym ' . $protein->ACRONYM . ' already exists in this proposal'); - $this->db->pq('INSERT INTO protein (proteinid,proposalid,name,acronym,sequence,molecularmass,bltimestamp,density,externalid) + $this->db->pq( + 'INSERT INTO protein (proteinid,proposalid,name,acronym,sequence,molecularmass,bltimestamp,density,externalid) VALUES (s_protein.nextval,:1,:2,:3,:4,:5,CURRENT_TIMESTAMP,:6,UNHEX(:7)) RETURNING proteinid INTO :id', - array($this->proposalid, $phaseName, $protein->ACRONYM, $phaseSeq, $phaseMass, $phaseDensity, $externalid)); - - if($isCapillary){ - $ids[$model]['CAPILLARYPHASEID'] = $this->db->id(); - $capillaryPhaseId = $this->db->id(); - } else { - $ids[$model]['PHASEID'] = $this->db->id(); - $ids[$model]['CAPILLARYPHASEID'] = $capillaryPhaseId; - } - $isCapillary = false; + array($this->proposalid, $phaseName, $protein->ACRONYM, $phaseSeq, $phaseMass, $phaseDensity, $externalid) + ); + + if ($isCapillary) { + $ids[$model]['CAPILLARYPHASEID'] = $this->db->id(); + $capillaryPhaseId = $this->db->id(); + } else { + $ids[$model]['PHASEID'] = $this->db->id(); + $ids[$model]['CAPILLARYPHASEID'] = $capillaryPhaseId; } + $isCapillary = false; + } - /** - * Insert Crystals - * The same as Proteins, in each iteration we need to check if we have Crystal information for both the actual crystal and its capillary (if required) - * If the crystal is to be containerless there will not be any crystal information for a capillary. - * - * Same logic on proteins applies to the file upload where $capillaryId holds the same single capillary id ready to associate it with all samples added in the transaction - * */ - $crystals = null; - if($attrs->fromFile) - $crystals = $capillaryId == null && !$capillary->CONTAINERLESS && $capillary->CRYSTALID == null ? array($capillary, $crystal) : array($crystal); - else - $crystals = $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS ? array($capillary, $crystal) : array($crystal); - - $isCapillary = sizeof($crystals) > 1 ? true : false; - - foreach($crystals as $sample){ - $c = array(); - foreach (array('SPACEGROUP', 'COMMENTS', 'NAME') as $f) $c[$f] = array_key_exists($f, $sample) ? $sample->$f : ''; - foreach (array('ABUNDANCE', 'THEORETICALDENSITY') as $f) $c[$f] = array_key_exists($f, $sample) ? $sample->$f : null; - - $pid = $isCapillary ? $ids[$model]['CAPILLARYPHASEID'] : $ids[$model]['PHASEID']; - - $this->db->pq("INSERT INTO crystal (crystalid,proteinid,spacegroup,abundance,comments,name,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6) RETURNING crystalid INTO :id", - array($pid, $c['SPACEGROUP'], $c['ABUNDANCE'], $c['COMMENTS'], $c['NAME'], $c['THEORETICALDENSITY'])); - - if($isCapillary){ - $ids[$model]['CAPILLARYID'] = $this->db->id(); - $capillaryId = $this->db->id(); - } else { - $ids[$model]['CRYSTALID'] = $this->db->id(); - } - $isCapillary = false; + /** + * Insert Crystals + * The same as Proteins, in each iteration we need to check if we have Crystal information for both the actual crystal and its capillary (if required) + * If the crystal is to be containerless there will not be any crystal information for a capillary. + * + * Same logic on proteins applies to the file upload where $capillaryId holds the same single capillary id ready to associate it with all samples added in the transaction + * */ + $crystals = null; + if ($attrs->fromFile) + $crystals = $capillaryId == null && !$capillary->CONTAINERLESS && $capillary->CRYSTALID == null ? array($capillary, $crystal) : array($crystal); + else + $crystals = $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS ? array($capillary, $crystal) : array($crystal); + + $isCapillary = sizeof($crystals) > 1 ? true : false; + + foreach ($crystals as $sample) { + $c = array(); + foreach (array('SPACEGROUP', 'COMMENTS', 'NAME') as $f) + $c[$f] = array_key_exists($f, $sample) ? $sample->$f : ''; + foreach (array('ABUNDANCE', 'THEORETICALDENSITY') as $f) + $c[$f] = array_key_exists($f, $sample) ? $sample->$f : null; + + $pid = $isCapillary ? $ids[$model]['CAPILLARYPHASEID'] : $ids[$model]['PHASEID']; + + $this->db->pq( + "INSERT INTO crystal (crystalid,proteinid,spacegroup,abundance,comments,name,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6) RETURNING crystalid INTO :id", + array($pid, $c['SPACEGROUP'], $c['ABUNDANCE'], $c['COMMENTS'], $c['NAME'], $c['THEORETICALDENSITY']) + ); + + if ($isCapillary) { + $ids[$model]['CAPILLARYID'] = $this->db->id(); + $capillaryId = $this->db->id(); + } else { + $ids[$model]['CRYSTALID'] = $this->db->id(); } + $isCapillary = false; + } - // Recently changed this so assumption is that request includes the default dewar id - // Avoids the need for a shared function. UI calls /dewar/default end point first - // No other info required about dewar so reuse of current endpoint preferred in this case - $ids[$model]['DEWARID'] = $attrs->DEWARID; - - // Do we have a container associated with this dewar? - $chk = $this->db->pq("SELECT containerid FROM container WHERE dewarid =:1", array($ids[$model]['DEWARID'])); - if (sizeof($chk)) $ids[$model]['CONTAINERID'] = $chk[0]['CONTAINERID']; - - // Insert Container if we don't already have one - if(!array_key_exists('CONTAINERID', $ids[$model])) { - $cap = array_key_exists('CAPACITY', $container) ? $container->CAPACITY : 16; - $com = array_key_exists('COMMENTS', $container) ? $container->COMMENTS : null; - - $this->db->pq("INSERT INTO container (containerid,dewarid,code,bltimestamp,capacity,containertype,comments) - VALUES (s_container.nextval,:1,:2,CURRENT_TIMESTAMP,:3,:4,:5) RETURNING containerid INTO :id", - array($ids[$model]['DEWARID'], $container->NAME, $cap, $container->CONTAINERTYPE, $com)); - $ids[$model]['CONTAINERID'] = $this->db->id(); - } + // Recently changed this so assumption is that request includes the default dewar id + // Avoids the need for a shared function. UI calls /dewar/default end point first + // No other info required about dewar so reuse of current endpoint preferred in this case + $ids[$model]['DEWARID'] = $attrs->DEWARID; + + // Do we have a container associated with this dewar? + $chk = $this->db->pq("SELECT containerid FROM container WHERE dewarid =:1", array($ids[$model]['DEWARID'])); + if (sizeof($chk)) + $ids[$model]['CONTAINERID'] = $chk[0]['CONTAINERID']; + + // Insert Container if we don't already have one + if (!array_key_exists('CONTAINERID', $ids[$model])) { + $cap = array_key_exists('CAPACITY', $container) ? $container->CAPACITY : 16; + $com = array_key_exists('COMMENTS', $container) ? $container->COMMENTS : null; + + $this->db->pq( + "INSERT INTO container (containerid,dewarid,code,bltimestamp,capacity,containertype,comments) + VALUES (s_container.nextval,:1,:2,CURRENT_TIMESTAMP,:3,:4,:5) RETURNING containerid INTO :id", + array($ids[$model]['DEWARID'], $container->NAME, $cap, $container->CONTAINERTYPE, $com) + ); + $ids[$model]['CONTAINERID'] = $this->db->id(); + } - // ADD BLSAMPLES - $blSamples = array(); - // In ISPyB a container can be various things, but for simple sample it is a box that can be imagined to have a grid layout - // We need to know which space the next sample needs to be added into. This query looks up the next free space - $maxloc_tmp = $this->db->pq("SELECT IFNULL((SELECT location FROM blsample WHERE containerid =:1 ORDER BY location * 1 DESC LIMIT 1),0) as location", array($ids[$model]['CONTAINERID'])); - $maxLocation = $maxloc_tmp[0]['LOCATION']; - - // Like Proteins and Crystals, we need to check if we need to add the BLSample related information for the capillary as well as the sample - // Also: LoopType = 1 means the BLSample is a container/capillary. Took some hunting to figure that out... - if(array_key_exists('CAPILLARYID', $ids[$model]) && $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS) - $blSamples['capillary'] = array('CONTAINERID' => $ids[$model]['CONTAINERID'], 'CRYSTALID' => $ids[$model]['CAPILLARYID'], 'PROTEINID' => $ids[$model]['CAPILLARYPHASEID'], 'LOCATION' => ++$maxLocation, 'NAME' => $capillary->NAME, 'PACKINGFRACTION' => 1, 'COMMENTS' => array_key_exists('COMMENTS', $capillary) ? $capillary->COMMENTS : '', 'DIMENSION1' => $capillary->OUTERDIAMETER, 'DIMENSION2' => $capillary->INNERDIAMETER, 'DIMENSION3' => $capillary->LENGTH, 'SHAPE' => $capillary->SHAPE, 'LOOPTYPE' => 1); - - $blSamples['sample'] = array('CONTAINERID' => $ids[$model]['CONTAINERID'], 'CRYSTALID' => $ids[$model]['CRYSTALID'], 'PROTEINID' => $ids[$model]['PHASEID'], 'LOCATION' => ++$maxLocation, 'NAME' => $crystal->NAME, 'PACKINGFRACTION' => $attrs->PACKINGFRACTION ? $attrs->PACKINGFRACTION : null, 'COMMENTS' => array_key_exists('COMMENTS', $crystal) ? $crystal->COMMENTS : ''); - - foreach($blSamples as $key => $blSample){ - $a = $this->_prepare_sample_args($blSample); - $this->db->pq("INSERT INTO blsample (blsampleid,crystalid,containerid,location,comments,name,code,packingfraction,dimension1,dimension2,dimension3,shape,looptype) VALUES (s_blsample.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12) RETURNING blsampleid INTO :id", - array($key == 'capillary' ? $ids[$model]['CAPILLARYID'] : $ids[$model]['CRYSTALID'], $a['CONTAINERID'], $a['LOCATION'], $a['COMMENTS'], $a['NAME'] ,$a['CODE'], $a['PACKINGFRACTION'], $a['DIMENSION1'], $a['DIMENSION2'], $a['DIMENSION3'], $a['SHAPE'], $a['LOOPTYPE'])); - - if($key == 'capillary'){ - $ids[$model]['BLSAMPLECAPILLARYID'] = $this->db->id(); - $blSampleCapillaryId = $this->db->id(); - } else { - $ids[$model]['BLSAMPLEID'] = $this->db->id(); - } + // ADD BLSAMPLES + $blSamples = array(); + // In ISPyB a container can be various things, but for simple sample it is a box that can be imagined to have a grid layout + // We need to know which space the next sample needs to be added into. This query looks up the next free space + $maxloc_tmp = $this->db->pq("SELECT IFNULL((SELECT location FROM blsample WHERE containerid =:1 ORDER BY location * 1 DESC LIMIT 1),0) as location", array($ids[$model]['CONTAINERID'])); + $maxLocation = $maxloc_tmp[0]['LOCATION']; + + // Like Proteins and Crystals, we need to check if we need to add the BLSample related information for the capillary as well as the sample + // Also: LoopType = 1 means the BLSample is a container/capillary. Took some hunting to figure that out... + if (array_key_exists('CAPILLARYID', $ids[$model]) && $capillary->CRYSTALID == null && !$capillary->CONTAINERLESS) + $blSamples['capillary'] = array('CONTAINERID' => $ids[$model]['CONTAINERID'], 'CRYSTALID' => $ids[$model]['CAPILLARYID'], 'PROTEINID' => $ids[$model]['CAPILLARYPHASEID'], 'LOCATION' => ++$maxLocation, 'NAME' => $capillary->NAME, 'PACKINGFRACTION' => 1, 'COMMENTS' => array_key_exists('COMMENTS', $capillary) ? $capillary->COMMENTS : '', 'DIMENSION1' => $capillary->OUTERDIAMETER, 'DIMENSION2' => $capillary->INNERDIAMETER, 'DIMENSION3' => $capillary->LENGTH, 'SHAPE' => $capillary->SHAPE, 'LOOPTYPE' => 1); + + $blSamples['sample'] = array('CONTAINERID' => $ids[$model]['CONTAINERID'], 'CRYSTALID' => $ids[$model]['CRYSTALID'], 'PROTEINID' => $ids[$model]['PHASEID'], 'LOCATION' => ++$maxLocation, 'NAME' => $crystal->NAME, 'PACKINGFRACTION' => $attrs->PACKINGFRACTION ? $attrs->PACKINGFRACTION : null, 'COMMENTS' => array_key_exists('COMMENTS', $crystal) ? $crystal->COMMENTS : ''); + + foreach ($blSamples as $key => $blSample) { + $a = $this->_prepare_sample_args($blSample); + $this->db->pq( + "INSERT INTO blsample (blsampleid,crystalid,containerid,location,comments,name,code,packingfraction,dimension1,dimension2,dimension3,shape,looptype) VALUES (s_blsample.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12) RETURNING blsampleid INTO :id", + array($key == 'capillary' ? $ids[$model]['CAPILLARYID'] : $ids[$model]['CRYSTALID'], $a['CONTAINERID'], $a['LOCATION'], $a['COMMENTS'], $a['NAME'], $a['CODE'], $a['PACKINGFRACTION'], $a['DIMENSION1'], $a['DIMENSION2'], $a['DIMENSION3'], $a['SHAPE'], $a['LOOPTYPE']) + ); + + if ($key == 'capillary') { + $ids[$model]['BLSAMPLECAPILLARYID'] = $this->db->id(); + $blSampleCapillaryId = $this->db->id(); + } else { + $ids[$model]['BLSAMPLEID'] = $this->db->id(); + } - /** - * Add DiffractionPlan (DataCollectionPlan DCP) information - * This is a set of inserts that collectively make up a DCP and associate it to a BLSample - * There are two detectors required for xpdf on I15-1 so we have one insert for each and the detector distance for each is an agreed constant - * The dectectors and detector distance values can be changed by users from the plan experiements page in SynchWeb - * */ - $this->db->pq("INSERT INTO diffractionplan (preferredbeamsizex, preferredbeamsizey, energy, monobandwidth) + /** + * Add DiffractionPlan (DataCollectionPlan DCP) information + * This is a set of inserts that collectively make up a DCP and associate it to a BLSample + * There are two detectors required for xpdf on I15-1 so we have one insert for each and the detector distance for each is an agreed constant + * The dectectors and detector distance values can be changed by users from the plan experiements page in SynchWeb + * */ + $this->db->pq("INSERT INTO diffractionplan (preferredbeamsizex, preferredbeamsizey, energy, monobandwidth) VALUES (:1, :2, :3, :4)", array($defaultBeamSizeX, $defaultBeamSizeY, $defaultEnergy, $defaultMonoBandwidth)); - if($key == 'capillary') - $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] = $this->db->id(); - else - $ids[$model]['DIFFRACTIONPLANID'] = $this->db->id(); + if ($key == 'capillary') + $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] = $this->db->id(); + else + $ids[$model]['DIFFRACTIONPLANID'] = $this->db->id(); - $expTime = $attrs->EXPOSURETIME ? $attrs->EXPOSURETIME : 600; + $expTime = $attrs->EXPOSURETIME ? $attrs->EXPOSURETIME : 600; - // Need to know the highest current DCP plan order so we can add new ones after it - $maxLocation = $this->_get_current_max_dcp_plan_order($ids[$model]['CONTAINERID']); + // Need to know the next DCP plan order index + $nextDCPIndex = $this->_next_dcp_plan_order_index($ids[$model]['CONTAINERID']); - $this->db->pq("INSERT INTO blsample_has_datacollectionplan (blsampleid, datacollectionplanid, planorder) - VALUES (:1, :2, :3)", array($key == 'capillary' ? $ids[$model]['BLSAMPLECAPILLARYID'] : $ids[$model]['BLSAMPLEID'], $key == 'capillary' ? $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] : $ids[$model]['DIFFRACTIONPLANID'], $maxLocation+1)); + $this->db->pq("INSERT INTO blsample_has_datacollectionplan (blsampleid, datacollectionplanid, planorder) + VALUES (:1, :2, :3)", array($key == 'capillary' ? $ids[$model]['BLSAMPLECAPILLARYID'] : $ids[$model]['BLSAMPLEID'], $key == 'capillary' ? $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] : $ids[$model]['DIFFRACTIONPLANID'], $nextDCPIndex)); - $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposureTime, distance) + $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposureTime, distance) VALUES (:1, :2, :3, :4)", array($key == 'capillary' ? $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] : $ids[$model]['DIFFRACTIONPLANID'], $detector1_id, $expTime, $detector1_distance)); - $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposureTime, distance) + $this->db->pq("INSERT INTO datacollectionplan_has_detector (datacollectionplanid, detectorid, exposureTime, distance) VALUES (:1, :2, :3, :4)", array($key == 'capillary' ? $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] : $ids[$model]['DIFFRACTIONPLANID'], $detector2_id, $expTime, $detector2_distance)); - $this->db->pq("INSERT INTO scanparametersmodel (scanparametersserviceid, datacollectionplanid, sequencenumber, start, stop, step) + $this->db->pq("INSERT INTO scanparametersmodel (scanparametersserviceid, datacollectionplanid, sequencenumber, start, stop, step) VALUES (:1, :2, :3, :4, :5, :6)", array(5, $key == 'capillary' ? $ids[$model]['CAPILLARYDIFFRACTIONPLANID'] : $ids[$model]['DIFFRACTIONPLANID'], 0, 0, 0, 1)); - } + } - /** - * Add Container Group - * Assuming a sample is not marked as containerless, we need to create a container group which is just an id number - * This container group id is used by BLSampleGroup_has_BLSample in a many to many relationship by mapping it to a BLSample id - * Doing this allows a sample to be associated with a capillary for experiment planning which can be useful for background subtraction - * */ - if(!$capillary->CONTAINERLESS){ - $this->db->pq("INSERT INTO blsamplegroup (blsamplegroupid, proposalid) VALUES(NULL, :1)", array($this->proposalid)); - $ids[$model]['SAMPLEGROUPID'] = $this->db->id(); - - if(!array_key_exists('BLSAMPLECAPILLARYID', $ids[$model])){ - if($attrs->fromFile && $capillaryId != null) { - $tmp_ids = $this->db->pq("SELECT blsampleid FROM blsample where crystalid = :1", array($capillaryId)); - $ids[$model]['BLSAMPLECAPILLARYID'] = $tmp_ids[0]['BLSAMPLEID']; - } else { - $tmp_ids = $this->db->pq("SELECT blsampleid FROM blsample where crystalid = :1", array($capillary->CRYSTALID)); - $ids[$model]['BLSAMPLECAPILLARYID'] = $tmp_ids[0]['BLSAMPLEID']; - } + /** + * Add Container Group + * Assuming a sample is not marked as containerless, we need to create a container group which is just an id number + * This container group id is used by BLSampleGroup_has_BLSample in a many to many relationship by mapping it to a BLSample id + * Doing this allows a sample to be associated with a capillary for experiment planning which can be useful for background subtraction + * */ + if (!$capillary->CONTAINERLESS) { + $this->db->pq("INSERT INTO blsamplegroup (blsamplegroupid, proposalid) VALUES(NULL, :1)", array($this->proposalid)); + $ids[$model]['SAMPLEGROUPID'] = $this->db->id(); + + if (!array_key_exists('BLSAMPLECAPILLARYID', $ids[$model])) { + if ($attrs->fromFile && $capillaryId != null) { + $tmp_ids = $this->db->pq("SELECT blsampleid FROM blsample where crystalid = :1", array($capillaryId)); + $ids[$model]['BLSAMPLECAPILLARYID'] = $tmp_ids[0]['BLSAMPLEID']; + } else { + $tmp_ids = $this->db->pq("SELECT blsampleid FROM blsample where crystalid = :1", array($capillary->CRYSTALID)); + $ids[$model]['BLSAMPLECAPILLARYID'] = $tmp_ids[0]['BLSAMPLEID']; } + } - $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) + $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) VALUES (:1,:2, :3, :4)", array($ids[$model]['BLSAMPLECAPILLARYID'], $ids[$model]['SAMPLEGROUPID'], 2, 'capillary')); - - $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) + + $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) VALUES (:1,:2, :3, :4)", array($ids[$model]['BLSAMPLEID'], $ids[$model]['SAMPLEGROUPID'], 1, 'sample')); - } + } - /** - * Insert CIF file(s) reference - * CIF files will be associated with ALL submitted samples - * */ - $fileCount = 0; - foreach($_FILES as $f){ - $fileRef = 'pdb_file_'.$fileCount; - $info = pathinfo($_FILES[$fileRef]['name']); - - if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') { - $file = file_get_contents($_FILES[$fileRef]['tmp_name']); - $this->_associate_pdb($info['basename'],$file,'',$ids[$model]['PHASEID']); - } - $fileCount++; + /** + * Insert CIF file(s) reference + * CIF files will be associated with ALL submitted samples + * */ + $fileCount = 0; + foreach ($_FILES as $f) { + $fileRef = 'pdb_file_' . $fileCount; + $info = pathinfo($_FILES[$fileRef]['name']); + + if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') { + $file = file_get_contents($_FILES[$fileRef]['tmp_name']); + $this->_associate_pdb($info['basename'], $file, '', $ids[$model]['PHASEID']); } + $fileCount++; } - $this->db->end_transaction(); - $this->_output($ids); } + $this->db->end_transaction(); + $this->_output($ids); } + } - function _pre_q_sub_sample() { - if (!$this->has_arg('BLSUBSAMPLEID')) $this->_error('No subsample specified'); - - if (is_array($this->arg('BLSUBSAMPLEID'))) { - $ret = array(); - foreach ($this->arg('BLSUBSAMPLEID') as $sid) { - array_push($ret, array('BLSUBSAMPLEID' => $sid, 'CONTAINERQUEUESAMPLEID' => $this->_do_pre_q_sample(array('BLSUBSAMPLEID' => $sid)))); - $this->_output($ret); - } + function _pre_q_sub_sample() + { + if (!$this->has_arg('BLSUBSAMPLEID')) + $this->_error('No subsample specified'); - } else { - $this->_output(array('CONTAINERQUEUESAMPLEID' => $this->_do_pre_q_sample(array('BLSUBSAMPLEID' => $this->arg('BLSUBSAMPLEID'))))); + if (is_array($this->arg('BLSUBSAMPLEID'))) { + $ret = array(); + foreach ($this->arg('BLSUBSAMPLEID') as $sid) { + array_push($ret, array('BLSUBSAMPLEID' => $sid, 'CONTAINERQUEUESAMPLEID' => $this->_do_pre_q_sample(array('BLSUBSAMPLEID' => $sid)))); + $this->_output($ret); } + } else { + $this->_output(array('CONTAINERQUEUESAMPLEID' => $this->_do_pre_q_sample(array('BLSUBSAMPLEID' => $this->arg('BLSUBSAMPLEID'))))); } + } - function _do_pre_q_sample($options) { - $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid FROM blsubsample ss + function _do_pre_q_sample($options) + { + $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -502,125 +533,164 @@ function _do_pre_q_sample($options) { INNER JOIN proposal p ON p.proposalid = sh.proposalid WHERE p.proposalid=:1 AND ss.blsubsampleid=:2", array($this->proposalid, $options['BLSUBSAMPLEID'])); - if (!sizeof($samp)) $this->_error('No such sub sample'); + if (!sizeof($samp)) + $this->_error('No such sub sample'); - if ($this->has_arg('UNQUEUE')) { - $this->db->pq("DELETE FROM containerqueuesample WHERE blsubsampleid=:1 AND containerqueueid IS NULL", array($options['BLSUBSAMPLEID'])); - - } else { - $this->db->pq("INSERT INTO containerqueuesample (blsubsampleid) VALUES (:1)", array($options['BLSUBSAMPLEID'])); - return $this->db->id(); - } + if ($this->has_arg('UNQUEUE')) { + $this->db->pq("DELETE FROM containerqueuesample WHERE blsubsampleid=:1 AND containerqueueid IS NULL", array($options['BLSUBSAMPLEID'])); + } else { + $this->db->pq("INSERT INTO containerqueuesample (blsubsampleid) VALUES (:1)", array($options['BLSUBSAMPLEID'])); + return $this->db->id(); } + } - function _add_sample_component() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('ABUNDANCE')) $this->_error('No amount specified'); - if (!$this->has_arg('COMPONENTID')) $this->_error('No component specified'); - if (!$this->has_arg('BLSAMPLETYPEID')) $this->_error('No crystal specified'); + function _add_sample_component() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('ABUNDANCE')) + $this->_error('No amount specified'); + if (!$this->has_arg('COMPONENTID')) + $this->_error('No component specified'); + if (!$this->has_arg('BLSAMPLETYPEID')) + $this->_error('No crystal specified'); - $check = $this->db->pq("SELECT crystalid FROM crystal c + $check = $this->db->pq("SELECT crystalid FROM crystal c INNER JOIN protein pr ON pr.proteinid = c.proteinid WHERE pr.proposalid=:1 AND c.crystalid=:2", array($this->proposalid, $this->arg('BLSAMPLETYPEID'))); - if (!sizeof($check)) $this->_error('No such blsampletype'); + if (!sizeof($check)) + $this->_error('No such blsampletype'); - $this->_update_sample_components(array(), array($this->arg('COMPONENTID')), array($this->arg('ABUNDANCE')), $this->arg('BLSAMPLETYPEID')); - $this->_output(1); - } + $this->_update_sample_components(array(), array($this->arg('COMPONENTID')), array($this->arg('ABUNDANCE')), $this->arg('BLSAMPLETYPEID')); + $this->_output(1); + } - function _remove_sample_component() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('scid')) $this->_error('No crystal/component specified'); + function _remove_sample_component() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('scid')) + $this->_error('No crystal/component specified'); - list($crystalid, $componentid) = explode('-', $this->arg('scid')); + list($crystalid, $componentid) = explode('-', $this->arg('scid')); - $check = $this->db->pq("SELECT crystalid FROM crystal c + $check = $this->db->pq("SELECT crystalid FROM crystal c INNER JOIN protein pr ON pr.proteinid = c.proteinid WHERE pr.proposalid=:1 AND c.crystalid=:2", array($this->proposalid, $crystalid)); - if (!sizeof($check)) $this->_error('No such blsampletype'); - - $this->_update_sample_components(array($componentid), array(), array(), $crystalid); - $this->_output(1); - } - + if (!sizeof($check)) + $this->_error('No such blsampletype'); - function _sub_samples() { - $where = ''; - $first_inner_select_where = ''; - $second_inner_select_where = ''; - $args = array($this->proposalid); + $this->_update_sample_components(array($componentid), array(), array(), $crystalid); + $this->_output(1); + } - if ($this->has_arg('sid')) { - $where .= ' AND s.blsampleid=:'.(sizeof($args)+1); - $first_inner_select_where .= ' AND s.blsampleid=:'.(sizeof($args) + 2); - $second_inner_select_where .= ' AND s.blsampleid=:'.(sizeof($args) + 3); - array_push($args, $this->arg('sid'), $this->arg('sid'), $this->arg('sid')); - } + function _queue_all_sub_samples() + { + if (!$this->has_arg('cid')) { + $this->_error('No containerid specified'); + } - if ($this->has_arg('cid')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - $first_inner_select_where .= ' AND s.containerid=:'.(sizeof($args) + 2); - $second_inner_select_where .= ' AND s.containerid=:'.(sizeof($args) + 3); - array_push($args, $this->arg('cid'), $this->arg('cid'), $this->arg('cid')); - } + $args = array($this->proposalid, $this->arg('cid'), $this->arg('cid'), $this->arg('cid')); + $where = ' AND c.containerid=:2 AND cq2.completedtimestamp IS NULL'; + $first_inner_select_where = ' AND s.containerid=:3'; + $second_inner_select_where = ' AND s.containerid=:4'; - $this->db->wait_rep_sync(true); - $ss_query_string = $this->get_sub_samples_query($where, $first_inner_select_where, $second_inner_select_where); - $subs = $this->db->pq($ss_query_string, $args); + $this->db->wait_rep_sync(true); + $ss_query_string = $this->get_sub_samples_query($where, $first_inner_select_where, $second_inner_select_where); + $subs = $this->db->pq($ss_query_string, $args); - $this->db->wait_rep_sync(false); + $this->db->wait_rep_sync(false); - foreach ($subs as $i => &$r) $r['RID'] = $i; + $ret = array(); + foreach ($subs as $sub) { + array_push($ret, array( + 'BLSUBSAMPLEID' => $sub['BLSUBSAMPLEID'], + 'CONTAINERQUEUESAMPLEID' => $this->_do_pre_q_sample(array('BLSUBSAMPLEID' => $sub['BLSUBSAMPLEID'])))); + } + $this->_output($ret); + } + + function _sub_samples() + { + $where = ''; + $first_inner_select_where = ''; + $second_inner_select_where = ''; + $args = array($this->proposalid); + + if ($this->has_arg('sid')) { + $where .= ' AND s.blsampleid=:' . (sizeof($args) + 1); + $first_inner_select_where .= ' AND s.blsampleid=:' . (sizeof($args) + 2); + $second_inner_select_where .= ' AND s.blsampleid=:' . (sizeof($args) + 3); + array_push($args, $this->arg('sid'), $this->arg('sid'), $this->arg('sid')); + } - $this->_output($subs); + if ($this->has_arg('cid')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + $first_inner_select_where .= ' AND s.containerid=:' . (sizeof($args) + 2); + $second_inner_select_where .= ' AND s.containerid=:' . (sizeof($args) + 3); + array_push($args, $this->arg('cid'), $this->arg('cid'), $this->arg('cid')); } - function _get_sub_sample() { - $where = ''; - $args = array($this->proposalid); + $this->db->wait_rep_sync(true); + $ss_query_string = $this->get_sub_samples_query($where, $first_inner_select_where, $second_inner_select_where); + $subs = $this->db->pq($ss_query_string, $args); - if ($this->has_arg('ssid')) { - $where .= ' AND ss.blsubsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('ssid')); - } + $this->db->wait_rep_sync(false); - $this->db->wait_rep_sync(true); - $ss_query_string = $this->get_sub_samples_query($where); - $subs = $this->db->pq($ss_query_string, $args); + foreach ($subs as $i => &$r) + $r['RID'] = $i; - $this->db->wait_rep_sync(false); + $this->_output($subs); + } - if (!sizeof($subs)) $this->_error('No such sub sample'); - else { - $subs[0]['RID'] = 0; - $this->_output($subs[0]); - } + function _get_sub_sample() + { + $where = ''; + $args = array($this->proposalid); + + if ($this->has_arg('ssid')) { + $where .= ' AND ss.blsubsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('ssid')); } - private function get_sub_samples_query($where, $first_inner_select_where = '', $second_inner_select_where = '') { - $group_by = ""; - $order_by = ""; - $having = ""; + $this->db->wait_rep_sync(true); + $ss_query_string = $this->get_sub_samples_query($where); + $subs = $this->db->pq($ss_query_string, $args); - if ($this->has_arg('queued')) { - $where .= ' AND cqs.containerqueuesampleid IS NOT NULL'; - } + $this->db->wait_rep_sync(false); - if ($this->has_arg('notcompleted')) { - $where .= ' AND cq2.completedtimestamp IS NULL'; - } + if (!sizeof($subs)) + $this->_error('No such sub sample'); + else { + $subs[0]['RID'] = 0; + $this->_output($subs[0]); + } + } - if ($this->has_arg('nodata')) { - $having .= ' HAVING count(dc.datacollectionid) = 0'; - } + private function get_sub_samples_query($where, $first_inner_select_where = '', $second_inner_select_where = '') + { + $group_by = ""; + $order_by = ""; + $having = ""; - if ($this->has_arg('ssid')) { - $from_query = "FROM blsubsample ss"; + if ($this->has_arg('queued')) { + $where .= ' AND cqs.containerqueuesampleid IS NOT NULL'; + } - } else { - $from_query = "FROM ( + if ($this->has_arg('notcompleted')) { + $where .= ' AND cq2.completedtimestamp IS NULL'; + } + + if ($this->has_arg('nodata')) { + $having .= ' HAVING count(dc.datacollectionid) = 0'; + } + + if ($this->has_arg('ssid')) { + $from_query = "FROM blsubsample ss"; + } else { + $from_query = "FROM ( SELECT ss.blsubsampleid FROM ( SELECT s.blsampleid, max(si.blsampleimageid) AS blsampleimageid @@ -640,7 +710,7 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $ WHERE ss.source = 'manual' $second_inner_select_where ) q JOIN blsubsample ss ON ss.blsubsampleid = q.blsubsampleid"; - $group_by = "GROUP BY pr.acronym, + $group_by = "GROUP BY pr.acronym, s.name, dp.experimentkind, dp.preferredbeamsizex, @@ -659,10 +729,10 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $ po.posy, po.posz"; - $order_by = "ORDER BY ss.blsubsampleid"; - } + $order_by = "ORDER BY ss.blsubsampleid"; + } - return "SELECT + return "SELECT pr.acronym as protein, s.name as sample, dp.experimentkind, @@ -749,12 +819,14 @@ private function get_sub_samples_query($where, $first_inner_select_where = '', $ $group_by $having $order_by"; - } + } - function _update_sub_sample() { - if (!$this->has_arg('ssid')) $this->_error('No subsample specified'); - - $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid,ss.position2id FROM blsubsample ss + function _update_sub_sample() + { + if (!$this->has_arg('ssid')) + $this->_error('No subsample specified'); + + $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid,ss.position2id FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -762,49 +834,52 @@ function _update_sub_sample() { INNER JOIN proposal p ON p.proposalid = sh.proposalid WHERE p.proposalid=:1 AND ss.blsubsampleid=:2", array($this->proposalid, $this->arg('ssid'))); - if (!sizeof($samp)) $this->_error('No such sub sample'); - - foreach(array('COMMENTS') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE blsubsample SET '.$f.'=:1 WHERE blsubsampleid=:2', array($this->arg($f), $this->arg('ssid'))); - $this->_output(array($f => $this->arg($f))); - } + if (!sizeof($samp)) + $this->_error('No such sub sample'); + + foreach (array('COMMENTS') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE blsubsample SET ' . $f . '=:1 WHERE blsubsampleid=:2', array($this->arg($f), $this->arg('ssid'))); + $this->_output(array($f => $this->arg($f))); } + } - if ($samp[0]['DIFFRACTIONPLANID']) { - foreach(array('REQUIREDRESOLUTION', 'EXPERIMENTKIND', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE diffractionplan SET '.$f.'=:1 WHERE diffractionplanid=:2', array($this->arg($f), $samp[0]['DIFFRACTIONPLANID'])); - $this->_output(array($f => $this->arg($f))); - } + if ($samp[0]['DIFFRACTIONPLANID']) { + foreach (array('REQUIREDRESOLUTION', 'EXPERIMENTKIND', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE diffractionplan SET ' . $f . '=:1 WHERE diffractionplanid=:2', array($this->arg($f), $samp[0]['DIFFRACTIONPLANID'])); + $this->_output(array($f => $this->arg($f))); } } + } - if ($samp[0]['POSITIONID']) { - foreach(array('X', 'Y', 'Z') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE position SET pos'.$f.'=:1 WHERE positionid=:2', array($this->arg($f), $samp[0]['POSITIONID'])); - $this->_output(array($f => $this->arg($f))); - } + if ($samp[0]['POSITIONID']) { + foreach (array('X', 'Y', 'Z') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE position SET pos' . $f . '=:1 WHERE positionid=:2', array($this->arg($f), $samp[0]['POSITIONID'])); + $this->_output(array($f => $this->arg($f))); } } + } - if ($samp[0]['POSITION2ID']) { - foreach(array('X2', 'Y2', 'Z2') as $f) { - if ($this->has_arg($f)) { - $cn = str_replace('2', '', $f); - $this->db->pq('UPDATE position SET pos'.$cn.'=:1 WHERE positionid=:2', array($this->arg($f), $samp[0]['POSITION2ID'])); - $this->_output(array($f => $this->arg($f))); - } + if ($samp[0]['POSITION2ID']) { + foreach (array('X2', 'Y2', 'Z2') as $f) { + if ($this->has_arg($f)) { + $cn = str_replace('2', '', $f); + $this->db->pq('UPDATE position SET pos' . $cn . '=:1 WHERE positionid=:2', array($this->arg($f), $samp[0]['POSITION2ID'])); + $this->_output(array($f => $this->arg($f))); } } } + } - function _update_sub_sample_full() { - if (!$this->arg('ssid')) $this->_error('No sub sample specified'); + function _update_sub_sample_full() + { + if (!$this->arg('ssid')) + $this->_error('No sub sample specified'); - $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid FROM blsubsample ss + $samp = $this->db->pq("SELECT ss.diffractionplanid,s.blsampleid,ss.positionid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -812,82 +887,95 @@ function _update_sub_sample_full() { INNER JOIN proposal p ON p.proposalid = sh.proposalid WHERE p.proposalid=:1 AND ss.blsubsampleid=:2", array($this->proposalid, $this->arg('ssid'))); - if (!sizeof($samp)) $this->_error('No such sub sample'); + if (!sizeof($samp)) + $this->_error('No such sub sample'); - if ($samp[0]['DIFFRACTIONPLANID']) { - $args = array($samp[0]['DIFFRACTIONPLANID']); - foreach(array('REQUIREDRESOLUTION', 'EXPERIMENTKIND', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', - 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR') as $f) { - array_push($args, $this->has_arg($f) ? $this->arg($f) : null); - } - $this->db->pq('UPDATE diffractionplan + if ($samp[0]['DIFFRACTIONPLANID']) { + $args = array($samp[0]['DIFFRACTIONPLANID']); + foreach (array( + 'REQUIREDRESOLUTION', 'EXPERIMENTKIND', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', + 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR' + ) as $f) { + array_push($args, $this->has_arg($f) ? $this->arg($f) : null); + } + $this->db->pq('UPDATE diffractionplan SET requiredresolution=:2, experimentkind=:3, preferredbeamsizex=:4, preferredbeamsizey=:5, exposuretime=:6, boxsizex=:7, boxsizey=:8, axisstart=:9, axisrange=:10, numberofimages=:11, transmission=:12, energy=:13, monochromator=:14 WHERE diffractionplanid=:1', $args); - $this->_output(array('BLSUBSAMPLEID' => $this->arg('ssid'))); - } + $this->_output(array('BLSUBSAMPLEID' => $this->arg('ssid'))); } + } - function _add_sub_sample() { - if (!$this->has_arg('BLSAMPLEID')) $this->_error('No sample specified'); - if (!$this->has_arg('X')) $this->_error('No x position specified'); - if (!$this->has_arg('Y')) $this->_error('No y position specified'); + function _add_sub_sample() + { + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); + if (!$this->has_arg('X')) + $this->_error('No x position specified'); + if (!$this->has_arg('Y')) + $this->_error('No y position specified'); - $z = $this->has_arg('Z') ? $this->arg('Z') : null; + $z = $this->has_arg('Z') ? $this->arg('Z') : null; - $x2 = $this->has_arg('X2') ? $this->arg('X2') : null; - $y2 = $this->has_arg('Y2') ? $this->arg('Y2') : null; - $z2 = $this->has_arg('Z2') ? $this->arg('Z2') : null; + $x2 = $this->has_arg('X2') ? $this->arg('X2') : null; + $y2 = $this->has_arg('Y2') ? $this->arg('Y2') : null; + $z2 = $this->has_arg('Z2') ? $this->arg('Z2') : null; - $samp = $this->db->pq("SELECT s.blsampleid FROM blsample s + $samp = $this->db->pq("SELECT s.blsampleid FROM blsample s INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping sh ON sh.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = sh.proposalid WHERE p.proposalid=:1 AND s.blsampleid=:2", array($this->proposalid, $this->arg('BLSAMPLEID'))); - if (!sizeof($samp)) $this->_error('No such sample'); + if (!sizeof($samp)) + $this->_error('No such sample'); - $this->db->pq("INSERT INTO position (positionid, posx, posy, posz) + $this->db->pq("INSERT INTO position (positionid, posx, posy, posz) VALUES (s_position.nextval, :1, :2, :3) RETURNING positionid INTO :id", array($this->arg('X'), $this->arg('Y'), $z)); - $pid = $this->db->id(); + $pid = $this->db->id(); - if ($x2 && $y2) { - $this->db->pq("INSERT INTO position (positionid, posx, posy, posz) + if ($x2 && $y2) { + $this->db->pq("INSERT INTO position (positionid, posx, posy, posz) VALUES (s_position.nextval, :1, :2, :3) RETURNING positionid INTO :id", array($x2, $y2, $z2)); - $pid2 = $this->db->id(); - } else $pid2 = null; + $pid2 = $this->db->id(); + } else + $pid2 = null; - $exp = ($x2 && $y2) ? 'MESH' : 'SAD'; - $this->db->pq("INSERT INTO diffractionplan (diffractionplanid,experimentkind) + $exp = ($x2 && $y2) ? 'MESH' : 'SAD'; + $this->db->pq("INSERT INTO diffractionplan (diffractionplanid,experimentkind) VALUES (s_diffractionplan.nextval,:1) RETURNING diffractionplanid INTO :id", array($exp)); - $did = $this->db->id(); + $did = $this->db->id(); - $this->db->pq("INSERT INTO blsubsample (blsubsampleid, blsampleid, positionid, position2id, diffractionplanid) + $this->db->pq("INSERT INTO blsubsample (blsubsampleid, blsampleid, positionid, position2id, diffractionplanid) VALUES (s_blsubsample.nextval, :1, :2, :3, :4) RETURNING blsubsampleid INTO :id", array($this->arg('BLSAMPLEID'), $pid, $pid2, $did)); - // $this->_output(array('BLSUBSAMPLEID' => $this->db->id())); - $this->args['ssid'] = $this->db->id(); - $this->_get_sub_sample(); - } + // $this->_output(array('BLSUBSAMPLEID' => $this->db->id())); + $this->args['ssid'] = $this->db->id(); + $this->_get_sub_sample(); + } - function _delete_sub_sample() { - if (!$this->has_arg('ssid')) $this->_error('No subsample specified'); + function _delete_sub_sample() + { + if (!$this->has_arg('ssid')) + $this->_error('No subsample specified'); - $can_delete = true; - $ref = null; - foreach (array('datacollection', 'energyscan', 'xfefluorescencespectrum', 'blsample') as $table) { - $chk = $this->db->pq("SELECT blsubsampleid FROM ${table} WHERE blsubsampleid=:1", array($this->arg('ssid'))); - if (sizeof($chk)) $can_delete = false; - $ref = $table; - } + $can_delete = true; + $ref = null; + foreach (array('datacollection', 'energyscan', 'xfefluorescencespectrum', 'blsample') as $table) { + $chk = $this->db->pq("SELECT blsubsampleid FROM ${table} WHERE blsubsampleid=:1", array($this->arg('ssid'))); + if (sizeof($chk)) + $can_delete = false; + $ref = $table; + } - if (!$can_delete) $this->_error('Cannot delete that subsample as it is referenced by another entity: '.$ref); + if (!$can_delete) + $this->_error('Cannot delete that subsample as it is referenced by another entity: ' . $ref); - $ssamp = $this->db->pq("SELECT ss.blsubsampleid FROM blsubsample ss + $ssamp = $this->db->pq("SELECT ss.blsubsampleid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -895,149 +983,160 @@ function _delete_sub_sample() { INNER JOIN proposal p ON p.proposalid = sh.proposalid WHERE p.proposalid=:1 AND ss.blsubsampleid=:2", array($this->proposalid, $this->arg('ssid'))); - if (!sizeof($ssamp)) $this->_error('No such subsample'); + if (!sizeof($ssamp)) + $this->_error('No such subsample'); - $this->db->pq("DELETE FROM blsubsample WHERE blsubsampleid=:1", array($this->arg('ssid'))); - $this->_output(1); - } + $this->db->pq("DELETE FROM blsubsample WHERE blsubsampleid=:1", array($this->arg('ssid'))); + $this->_output(1); + } - # ------------------------------------------------------------------------ - # List of samples for a proposal - function _samples() { - // ini_set('memory_limit', '512M'); - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $args = array($this->proposalid); - $where = 'pr.proposalid=:1'; - $having = ''; - $join = ''; - - # For a specific project - if ($this->has_arg('pjid')) { - $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personid, $this->user->personid)); - if (!sizeof($info)) $this->_error('No such project'); - - $args = array($this->arg('pjid')); - $where = '(pj.projectid=:'.sizeof($args).')'; - $join = ' LEFT OUTER JOIN project_has_blsample pj ON pj.blsampleid=b.blsampleid'; - - if (!$this->staff) { - $join .= " INNER JOIN blsession ses ON ses.proposalid = p.proposalid - INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:".(sizeof($args)+1); + # ------------------------------------------------------------------------ + # List of samples for a proposal + function _samples() + { + // ini_set('memory_limit', '512M'); + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - array_push($args, $this->user->personid); - } - - if ($this->has_arg('imp')) { - if ($this->arg('imp')) { - array_push($args, $this->arg('pjid')); - $join .= ' LEFT OUTER JOIN project_has_protein pji ON pji.proteinid=pr.proteinid'; - $where = preg_replace('/\(pj/', '(pji.projectid=:'.sizeof($args).' OR pj', $where); - } - } - } - - # For a specific protein - if ($this->has_arg('pid')) { - $where .= ' AND (pr.proteinid=:'.(sizeof($args)+1).' OR chc2.componentid=:'.(sizeof($args)+2).')'; - $join .= ' LEFT OUTER JOIN blsampletype_has_component chc2 ON chc2.blsampletypeid=b.crystalid'; - array_push($args, $this->arg('pid')); - array_push($args, $this->arg('pid')); - } + $args = array($this->proposalid); + $where = 'pr.proposalid=:1'; + $having = ''; + $join = ''; - # For a particular crystal - if ($this->has_arg('crid')) { - $where .= ' AND cr.crystalid=:'.(sizeof($args)+1); - array_push($args, $this->arg('crid')); - } + # For a specific project + if ($this->has_arg('pjid')) { + $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personId, $this->user->personId)); + if (!sizeof($info)) + $this->_error('No such project'); - # Sample group - if ($this->has_arg('BLSAMPLEGROUPID')) { - $where .= ' AND bsg.blsamplegroupid =:'.(sizeof($args)+1); - array_push($args, $this->arg('BLSAMPLEGROUPID')); - } + $args = array($this->arg('pjid')); + $where = '(pj.projectid=:' . sizeof($args) . ')'; + $join = ' LEFT OUTER JOIN project_has_blsample pj ON pj.blsampleid=b.blsampleid'; - # For a specific container - if ($this->has_arg('cid')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('cid')); - } - - # For a particular sample - if ($this->has_arg('sid')) { - $where .= ' AND b.blsampleid=:'.(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } + if (!$this->staff) { + $join .= " INNER JOIN blsession ses ON ses.proposalid = p.proposalid + INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:" . (sizeof($args) + 1); - # For a loop type - if ($this->has_arg('lt')) { - $where .= ' AND b.looptype LIKE :'.(sizeof($args)+1); - array_push($args, $this->arg('lt')); - } - - - # For a visit - if ($this->has_arg('visit')) { - $info = $this->db->pq("SELECT s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1", array($this->arg('visit'))); - - if (!sizeof($info)) $this->_error('No such visit'); - else $info = $info[0]; - - $where .= " AND d.dewarstatus='processing' AND c.beamlinelocation LIKE :".(sizeof($args)+1)." AND c.samplechangerlocation is NOT NULL"; - array_push($args, $info['BL']); + array_push($args, $this->user->personId); } - - $cseq = ''; - $sseq = ''; - if ($this->has_arg('seq')) { - $cseq = 'string_agg(cpr.sequence) as componentsequences,'; - $sseq = 'pr.sequence,'; + if ($this->has_arg('imp')) { + if ($this->arg('imp')) { + array_push($args, $this->arg('pjid')); + $join .= ' LEFT OUTER JOIN project_has_protein pji ON pji.proteinid=pr.proteinid'; + $where = preg_replace('/\(pj/', '(pji.projectid=:' . sizeof($args) . ' OR pj', $where); + } } - - # Collected during visit - if ($this->has_arg('collected_during')) { - $visit = $this->db->pq("SELECT s.sessionid + } + + # For a specific protein + if ($this->has_arg('pid')) { + $where .= ' AND (pr.proteinid=:' . (sizeof($args) + 1) . ' OR chc2.componentid=:' . (sizeof($args) + 2) . ')'; + $join .= ' LEFT OUTER JOIN blsampletype_has_component chc2 ON chc2.blsampletypeid=b.crystalid'; + array_push($args, $this->arg('pid')); + array_push($args, $this->arg('pid')); + } + + # For a particular crystal + if ($this->has_arg('crid')) { + $where .= ' AND cr.crystalid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('crid')); + } + + # Sample group + if ($this->has_arg('BLSAMPLEGROUPID')) { + $where .= ' AND bsg.blsamplegroupid =:' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEGROUPID')); + } + + # For a specific container + if ($this->has_arg('cid')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('cid')); + } + + # For a particular sample + if ($this->has_arg('sid')) { + $where .= ' AND b.blsampleid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + } + + # For a loop type + if ($this->has_arg('lt')) { + $where .= ' AND b.looptype LIKE :' . (sizeof($args) + 1); + array_push($args, $this->arg('lt')); + } + + + # For a visit + if ($this->has_arg('visit')) { + $info = $this->db->pq("SELECT s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1", array($this->arg('visit'))); + + if (!sizeof($info)) + $this->_error('No such visit'); + else + $info = $info[0]; + + $where .= " AND d.dewarstatus='processing' AND c.beamlinelocation LIKE :" . (sizeof($args) + 1) . " AND c.samplechangerlocation is NOT NULL AND c.samplechangerlocation != ''"; + array_push($args, $info['BL']); + } + + + $cseq = ''; + $sseq = ''; + if ($this->has_arg('seq')) { + $cseq = 'string_agg(cpr.sequence) as componentsequences,'; + $sseq = 'pr.sequence,'; + } + + # Collected during visit + if ($this->has_arg('collected_during')) { + $visit = $this->db->pq( + "SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1 AND p.proposalid=:2", - array($this->arg('collected_during'), $this->proposalid)); + array($this->arg('collected_during'), $this->proposalid) + ); - if (!sizeof($visit)) $this->_error("No such visit"); - $sessionid = $visit[0]["SESSIONID"]; + if (!sizeof($visit)) + $this->_error("No such visit"); + $sessionid = $visit[0]["SESSIONID"]; - $join .= " LEFT OUTER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid"; - $where .= " AND (r.blsessionid=:".(sizeof($args)+1)." OR dcg.sessionid=:".(sizeof($args)+2).")"; - array_push($args, $sessionid); - array_push($args, $sessionid); - } - - // Search - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(b.name) LIKE lower(CONCAT(CONCAT('%',:".$st."),'%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) OR lower(b.comments) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')) OR lower(b.code) LIKE lower(CONCAT(CONCAT('%',:".($st+3)."), '%')))"; - for ($i = 0; $i < 4; $i++) array_push($args, $this->arg('s')); - } - - - // Filter by sample status - if ($this->has_arg('t')) { - //$this->db->set_debug(true); - $types = array('R' => 'count(distinct r.robotactionid)', - 'SC' => 'count(distinct IF(dc.overlap != 0,dc.datacollectionid,NULL))', - 'AI' => 'count(distinct so.screeningid)', - 'DC' => 'count(distinct IF(dc.overlap = 0 AND dc.axisrange > 0,dc.datacollectionid,NULL))', - 'AP' => 'count(distinct ap.autoprocintegrationid)'); - if (array_key_exists($this->arg('t'), $types)) { - $having .= " HAVING ".$types[$this->arg('t')]." > 0"; - } + $join .= " LEFT OUTER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid"; + $where .= " AND (r.blsessionid=:" . (sizeof($args) + 1) . " OR dcg.sessionid=:" . (sizeof($args) + 2) . ")"; + array_push($args, $sessionid); + array_push($args, $sessionid); + } + + // Search + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(b.name) LIKE lower(CONCAT(CONCAT('%',:" . $st . "),'%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) OR lower(b.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')) OR lower(b.code) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 3) . "), '%')))"; + for ($i = 0; $i < 4; $i++) + array_push($args, $this->arg('s')); + } + + // Filter by sample status + if ($this->has_arg('t')) { + //$this->db->set_debug(true); + $types = array( + 'R' => 'count(distinct r.robotactionid)', + 'SC' => 'count(distinct IF(dc.overlap != 0,dc.datacollectionid,NULL))', + 'AI' => 'count(distinct so.screeningid)', + 'DC' => 'count(distinct IF(dc.overlap = 0 AND dc.axisrange > 0,dc.datacollectionid,NULL))', + 'AP' => 'count(distinct ap.autoprocintegrationid)' + ); + if (array_key_exists($this->arg('t'), $types)) { + $having .= " HAVING " . $types[$this->arg('t')] . " > 0"; } + } - $tot = $this->db->pq("SELECT count(distinct b.blsampleid) as tot + $tot = $this->db->pq("SELECT count(distinct b.blsampleid) as tot FROM blsample b INNER JOIN crystal cr ON cr.crystalid = b.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid @@ -1049,39 +1148,39 @@ function _samples() { LEFT OUTER JOIN datacollection dc ON b.blsampleid = dc.blsampleid LEFT OUTER JOIN robotaction r ON r.blsampleid = b.blsampleid AND r.actiontype = 'LOAD' $join WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'b.blsampleid DESC'; - - - if ($this->has_arg('sort_by')) { - $cols = array('SAMPLEID' => 'b.blsampleid', 'NAME' => 'b.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'b.comments', 'SHIPMENT' => 'shipment', 'DEWAR' => 'dewar', 'CONTAINER' => 'container', 'b.blsampleid', 'SC' => 'sc', 'SCRESOLUTION' => 'scresolution', 'DC' => 'ap', 'DCRESOLUTION' => 'dcresolution', 'POSITION' => 'TO_NUMBER(b.location)', 'RECORDTIMESTAMP' => 'b.recordtimestamp'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $rows = $this->db->paginate("SELECT distinct b.blsampleid, b.crystalid, b.screencomponentgroupid, ssp.blsampleid as parentsampleid, ssp.name as parentsample, b.blsubsampleid, count(distinct si.blsampleimageid) as inspections, CONCAT(p.proposalcode,p.proposalnumber) as prop, b.code, b.location, pr.acronym, pr.proteinid, cr.spacegroup,b.comments,b.name,s.shippingname as shipment,s.shippingid,d.dewarid,d.code as dewar, c.code as container, c.containerid, c.samplechangerlocation as sclocation, count(distinct IF(dc.overlap != 0,dc.datacollectionid,NULL)) as sc, count(distinct IF(dc.overlap = 0 AND dc.axisrange = 0,dc.datacollectionid,NULL)) as gr, count(distinct IF(dc.overlap = 0 AND dc.axisrange > 0,dc.datacollectionid,NULL)) as dc, count(distinct IF(dcg.experimenttype LIKE 'XRF map', dc.datacollectionid, NULL)) as xm, count(distinct IF(dcg.experimenttype LIKE 'XRF spectrum', dc.datacollectionid, NULL)) as xs, count(distinct IF(dcg.experimenttype LIKE 'Energy scan', dc.datacollectionid, NULL)) as es, count(distinct so.screeningid) as ai, count(distinct app.autoprocprogramid) as ap, count(distinct r.robotactionid) as r, round(min(st.rankingresolution),2) as scresolution, max(ssw.completeness) as sccompleteness, round(min(apss.resolutionlimithigh),2) as dcresolution, round(max(apss.completeness),1) as dccompleteness, dp.anomalousscatterer, dp.requiredresolution, cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma, b.packingfraction, b.dimension1, b.dimension2, b.dimension3, b.shape, cr.theoreticaldensity, cr.name as crystal, pr.name as protein, b.looptype, dp.centringmethod, dp.experimentkind, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp + + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'b.blsampleid DESC'; + + + if ($this->has_arg('sort_by')) { + $cols = array('SAMPLEID' => 'b.blsampleid', 'NAME' => 'b.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'b.comments', 'SHIPMENT' => 'shipment', 'DEWAR' => 'dewar', 'CONTAINER' => 'container', 'b.blsampleid', 'SC' => 'sc', 'SCRESOLUTION' => 'scresolution', 'DC' => 'ap', 'DCRESOLUTION' => 'dcresolution', 'POSITION' => 'TO_NUMBER(b.location)', 'RECORDTIMESTAMP' => 'b.recordtimestamp'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + + $rows = $this->db->paginate("SELECT distinct b.blsampleid, b.crystalid, b.screencomponentgroupid, ssp.blsampleid as parentsampleid, ssp.name as parentsample, b.blsubsampleid, count(distinct si.blsampleimageid) as inspections, CONCAT(p.proposalcode,p.proposalnumber) as prop, b.code, b.location, pr.acronym, pr.proteinid, cr.spacegroup,b.comments,b.name,s.shippingname as shipment,s.shippingid,d.dewarid,d.code as dewar, c.code as container, c.containerid, c.samplechangerlocation as sclocation, count(distinct IF(dc.overlap != 0,dc.datacollectionid,NULL)) as sc, count(distinct IF(dc.overlap = 0 AND dc.axisrange = 0,dc.datacollectionid,NULL)) as gr, count(distinct IF(dc.overlap = 0 AND dc.axisrange > 0,dc.datacollectionid,NULL)) as dc, count(distinct IF(dcg.experimenttype LIKE 'XRF map', dc.datacollectionid, NULL)) as xm, count(distinct IF(dcg.experimenttype LIKE 'XRF spectrum', dc.datacollectionid, NULL)) as xs, count(distinct IF(dcg.experimenttype LIKE 'Energy scan', dc.datacollectionid, NULL)) as es, count(distinct so.screeningid) as ai, count(distinct app.autoprocprogramid) as ap, count(distinct r.robotactionid) as r, round(min(st.rankingresolution),2) as scresolution, max(ssw.completeness) as sccompleteness, round(min(apss.resolutionlimithigh),2) as dcresolution, round(max(apss.completeness),1) as dccompleteness, dp.anomalousscatterer, dp.requiredresolution, cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma, b.packingfraction, b.dimension1, b.dimension2, b.dimension3, b.shape, cr.color, cr.theoreticaldensity, cr.name as crystal, pr.name as protein, b.looptype, dp.centringmethod, dp.experimentkind, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp , $cseq $sseq string_agg(cpr.name) as componentnames, string_agg(cpr.density) as componentdensities ,string_agg(cpr.proteinid) as componentids, string_agg(cpr.acronym) as componentacronyms, string_agg(cpr.global) as componentglobals, string_agg(chc.abundance) as componentamounts, string_agg(ct.symbol) as componenttypesymbols, b.volume, pct.symbol,ROUND(cr.abundance,3) as abundance, TO_CHAR(b.recordtimestamp, 'DD-MM-YYYY') as recordtimestamp, dp.radiationsensitivity, dp.energy, dp.userpath, dp.strategyoption, dp.minimalresolution as minimumresolution ,count(distinct dc.dataCollectionId) as dcc - FROM blsample b @@ -1109,7 +1208,6 @@ function _samples() { LEFT OUTER JOIN screeningstrategy st ON st.screeningoutputid = so.screeningoutputid AND sc.shortcomments LIKE '%EDNA%' LEFT OUTER JOIN screeningstrategywedge ssw ON ssw.screeningstrategyid = st.screeningstrategyid - LEFT OUTER JOIN autoprocintegration ap ON ap.datacollectionid = dc.datacollectionid LEFT OUTER JOIN autoprocscaling_has_int aph ON aph.autoprocintegrationid = ap.autoprocintegrationid LEFT OUTER JOIN autoprocscalingstatistics apss ON apss.autoprocscalingid = aph.autoprocscalingid @@ -1120,7 +1218,6 @@ function _samples() { LEFT OUTER JOIN blsubsample ss ON b.blsubsampleid = ss.blsubsampleid AND ss.source='manual' LEFT OUTER JOIN blsample ssp ON ss.blsampleid = ssp.blsampleid - LEFT OUTER JOIN robotaction r ON r.blsampleid = b.blsampleid AND r.actiontype = 'LOAD' $join @@ -1132,42 +1229,48 @@ function _samples() { $having ORDER BY $order", $args); - - foreach ($rows as &$r) { - foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS', 'COMPONENTNAMES', 'COMPONENTDENSITIES', 'COMPONENTSEQUENCES') as $k) { - if (array_key_exists($k, $r)) { - if ($r[$k]) $r[$k] = explode(',', $r[$k]); - } - } - // display DCP count for each sample - if($this->has_arg('dcp')){ - $dcpCount = $this->db->pq("SELECT COUNT(*) AS DCPCOUNT FROM BLSample_has_DataCollectionPlan WHERE blSampleId =:1", array($r['BLSAMPLEID'])); - $r['DCPCOUNT'] = $dcpCount[0]['DCPCOUNT']; + foreach ($rows as &$r) { + foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS', 'COMPONENTNAMES', 'COMPONENTDENSITIES', 'COMPONENTSEQUENCES') as $k) { + if (array_key_exists($k, $r)) { + if ($r[$k]) + $r[$k] = explode(',', $r[$k]); } } + // display DCP count for each sample + if ($this->has_arg('dcp')) { + $dcpCount = $this->db->pq("SELECT COUNT(*) AS DCPCOUNT FROM BLSample_has_DataCollectionPlan WHERE blSampleId =:1", array($r['BLSAMPLEID'])); + $r['DCPCOUNT'] = $dcpCount[0]['DCPCOUNT']; + } + } - if ($this->has_arg('sid')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such sample'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); + if ($this->has_arg('sid')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such sample'); + } else { + $this->_output(array('total' => $tot, 'data' => $rows)); } + } - function _update_sample_full() { - $a = $this->_prepare_strategy_option_for_sample($this->_prepare_sample_args()); - $result = $this->_handle_update_sample_full($a, $this->arg('sid')); + function _update_sample_full() + { + $a = $this->_prepare_strategy_option_for_sample($this->_prepare_sample_args()); + $result = $this->_handle_update_sample_full($a, $this->arg('sid')); + + $this->_output($result); + } - $this->_output($result); - } - - function _handle_update_sample_full($a, $sid) { - if (empty($sid)) $this->_error('No sampleid provided'); + function _handle_update_sample_full($a, $sid) + { + if (empty($sid)) + $this->_error('No sampleid provided'); - $samp = $this->db->pq("SELECT sp.blsampleid, pr.proteinid, cr.crystalid, dp.diffractionplanid, string_agg(chc.componentid) as componentids + $samp = $this->db->pq( + "SELECT sp.blsampleid, pr.proteinid, cr.crystalid, dp.diffractionplanid, string_agg(chc.componentid) as componentids FROM blsample sp INNER JOIN crystal cr ON sp.crystalid = cr.crystalid INNER JOIN protein pr ON cr.proteinid = pr.proteinid @@ -1176,360 +1279,454 @@ function _handle_update_sample_full($a, $sid) { LEFT OUTER JOIN diffractionplan dp ON dp.diffractionplanid = sp.diffractionplanid WHERE p.proposalid = :1 AND sp.blsampleid=:2 GROUP BY sp.blsampleid, pr.proteinid, cr.crystalid, dp.diffractionplanid", - array($this->proposalid, $sid)); - - if (!sizeof($samp)) $this->_error('No such sample'); - else $samp = $samp[0]; - - $blSampleId = $samp['BLSAMPLEID']; - - $this->db->pq("UPDATE blsample set name=:1,comments=:2,code=:3,volume=:4,packingfraction=:5,dimension1=:6,dimension2=:7,dimension3=:8,shape=:9,looptype=:10 WHERE blsampleid=:11", - array($a['NAME'],$a['COMMENTS'],$a['CODE'],$a['VOLUME'],$a['PACKINGFRACTION'],$a['DIMENSION1'],$a['DIMENSION2'],$a['DIMENSION3'],$a['SHAPE'],$a['LOOPTYPE'], $blSampleId)); - - if (array_key_exists('PROTEINID', $a)) { - $this->db->pq("UPDATE crystal set spacegroup=:1,proteinid=:2,cell_a=:3,cell_b=:4,cell_c=:5,cell_alpha=:6,cell_beta=:7,cell_gamma=:8,theoreticaldensity=:9 WHERE crystalid=:10", - array($a['SPACEGROUP'], $a['PROTEINID'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['THEORETICALDENSITY'], $samp['CRYSTALID'])); - $this->db->pq("UPDATE diffractionplan set anomalousscatterer=:1,requiredresolution=:2, experimentkind=:3, centringmethod=:4, radiationsensitivity=:5, energy=:6, userpath=:7, strategyoption=:8, minimalresolution=:9 WHERE diffractionplanid=:10", - array($a['ANOMALOUSSCATTERER'], $a['REQUIREDRESOLUTION'], $a['EXPERIMENTKIND'], $a['CENTRINGMETHOD'], $a['RADIATIONSENSITIVITY'], $a['ENERGY'], $a['USERPATH'], $a['STRATEGYOPTION'], $a['MINIMUMRESOLUTION'], $samp['DIFFRACTIONPLANID'])); - - if (!isset($a['INITIALSAMPLEGROUP']) && $a['VALID_SAMPLE_GROUP'] && $sid) { - $this->_save_sample_to_group($sid, $a['SAMPLEGROUP'], null, null); - } else if (isset($a['INITIALSAMPLEGROUP']) && !$a['SAMPLEGROUP'] && isset($blSampleId)) { - $this->_delete_sample_from_group($a['INITIALSAMPLEGROUP'], $blSampleId); - } - } - - $init_comps = explode(',', $samp['COMPONENTIDS']); - $fin_comps = $a['COMPONENTIDS'] ? $a['COMPONENTIDS'] : array(); - $amounts = $a['COMPONENTAMOUNTS'] ? $a['COMPONENTAMOUNTS'] : null; - $this->_update_sample_components($init_comps, $fin_comps, $amounts, $samp['CRYSTALID']); + array($this->proposalid, $sid) + ); - return array('BLSAMPLEID' => $samp['BLSAMPLEID']); - } + if (!sizeof($samp)) + $this->_error('No such sample'); + else + $samp = $samp[0]; - function _update_sample_components($initial, $final, $amounts, $crystalid) { - $rem = array_diff($initial, $final); - $add = array_diff($final, $initial); + $blSampleId = $samp['BLSAMPLEID']; - foreach ($rem as $r) $this->db->pq("DELETE FROM blsampletype_has_component WHERE blsampletypeid=:1 AND componentid=:2", array($crystalid, $r)); - foreach ($add as $a) $this->db->pq("INSERT INTO blsampletype_has_component (blsampletypeid, componentid) VALUES (:1,:2)", array($crystalid, $a)); + $this->db->pq( + "UPDATE blsample set name=:1,comments=:2,code=:3,volume=:4,packingfraction=:5,dimension1=:6,dimension2=:7,dimension3=:8,shape=:9,looptype=:10 WHERE blsampleid=:11", + array($a['NAME'], $a['COMMENTS'], $a['CODE'], $a['VOLUME'], $a['PACKINGFRACTION'], $a['DIMENSION1'], $a['DIMENSION2'], $a['DIMENSION3'], $a['SHAPE'], $a['LOOPTYPE'], $blSampleId) + ); - if ($amounts) { - foreach($final as $i => $f) { - $this->db->pq("UPDATE blsampletype_has_component SET abundance=:1 WHERE blsampletypeid=:2 AND componentid=:3", array($amounts[$i], $crystalid, $f)); - } + if (array_key_exists('PROTEINID', $a)) { + $this->db->pq( + "UPDATE crystal set spacegroup=:1,proteinid=:2,cell_a=:3,cell_b=:4,cell_c=:5,cell_alpha=:6,cell_beta=:7,cell_gamma=:8,theoreticaldensity=:9 WHERE crystalid=:10", + array($a['SPACEGROUP'], $a['PROTEINID'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['THEORETICALDENSITY'], $samp['CRYSTALID']) + ); + $this->db->pq( + "UPDATE diffractionplan set anomalousscatterer=:1,requiredresolution=:2, experimentkind=:3, centringmethod=:4, radiationsensitivity=:5, energy=:6, userpath=:7, strategyoption=:8, minimalresolution=:9 WHERE diffractionplanid=:10", + array($a['ANOMALOUSSCATTERER'], $a['REQUIREDRESOLUTION'], $a['EXPERIMENTKIND'], $a['CENTRINGMETHOD'], $a['RADIATIONSENSITIVITY'], $a['ENERGY'], $a['USERPATH'], $a['STRATEGYOPTION'], $a['MINIMUMRESOLUTION'], $samp['DIFFRACTIONPLANID']) + ); + + if (!isset($a['INITIALSAMPLEGROUP']) && $a['VALID_SAMPLE_GROUP'] && $sid) { + $this->_save_sample_to_group($sid, $a['SAMPLEGROUP'], null, null); + } else if (isset($a['INITIALSAMPLEGROUP']) && !$a['SAMPLEGROUP'] && isset($blSampleId)) { + $this->_delete_sample_from_group($a['INITIALSAMPLEGROUP'], $blSampleId); } } + $init_comps = explode(',', $samp['COMPONENTIDS']); + $fin_comps = $a['COMPONENTIDS'] ? $a['COMPONENTIDS'] : array(); + $amounts = $a['COMPONENTAMOUNTS'] ? $a['COMPONENTAMOUNTS'] : null; + $this->_update_sample_components($init_comps, $fin_comps, $amounts, $samp['CRYSTALID']); - function _add_sample() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - // Register entire container - if ($this->has_arg('collection')) { - $this->db->start_transaction(); - $col = array(); - foreach ($this->arg('collection') as $s) { - $id = $this->_do_add_sample($this->_prepare_sample_args($s)); + return array('BLSAMPLEID' => $samp['BLSAMPLEID']); + } - if ($id) { - $s['BLSAMPLEID'] = $id; - array_push($col, $s); - } - } + function _update_sample_components($initial, $final, $amounts, $crystalid) + { + $rem = array_diff($initial, $final); + $add = array_diff($final, $initial); - $this->db->end_transaction(); - $this->user->set_cache('container', null); - $this->_output($col); + foreach ($rem as $r) + $this->db->pq("DELETE FROM blsampletype_has_component WHERE blsampletypeid=:1 AND componentid=:2", array($crystalid, $r)); + foreach ($add as $a) + $this->db->pq("INSERT INTO blsampletype_has_component (blsampletypeid, componentid) VALUES (:1,:2)", array($crystalid, $a)); - // Register single sample - } else { - $id = $this->_do_add_sample($this->_prepare_sample_args()); - $this->_output(array('BLSAMPLEID' => $id)); + if ($amounts) { + foreach ($final as $i => $f) { + $this->db->pq("UPDATE blsampletype_has_component SET abundance=:1 WHERE blsampletypeid=:2 AND componentid=:3", array($amounts[$i], $crystalid, $f)); } } + } - function _bulk_update_sample_full() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - // Register entire container + function _add_sample() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + // Register entire container + if ($this->has_arg('collection')) { $this->db->start_transaction(); $col = array(); foreach ($this->arg('collection') as $s) { - $sample = $this->_prepare_strategy_option_for_sample($this->_prepare_sample_args($s)); - $result = $this->_handle_update_sample_full($sample, $s['BLSAMPLEID']); + $id = $this->_do_add_sample($this->_prepare_sample_args($s)); - if ($result) { - array_push($col, $result); + if ($id) { + $s['BLSAMPLEID'] = $id; + array_push($col, $s); } } $this->db->end_transaction(); + $this->user->setInCache('container', null); $this->_output($col); + + // Register single sample + } else { + $id = $this->_do_add_sample($this->_prepare_sample_args()); + $this->_output(array('BLSAMPLEID' => $id)); } + } + function _bulk_update_sample_full() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - function _prepare_sample_args($s=null) { - $a = array(); - foreach (array('LOCATION', 'CONTAINERID', 'NAME') as $f) { - if ($s) { - if (!array_key_exists($f, $s)) $this->_error('One or more fields are mising'); - else $a[$f] = $s[$f]; + // Register entire container + $this->db->start_transaction(); + $col = array(); + foreach ($this->arg('collection') as $s) { + $sample = $this->_prepare_strategy_option_for_sample($this->_prepare_sample_args($s)); + $result = $this->_handle_update_sample_full($sample, $s['BLSAMPLEID']); - } else { - if (!$this->has_arg($f)) $this->_error('One or more fields are mising'); - else $a[$f] = $this->arg($f); - } + if ($result) { + array_push($col, $result); } + } - $haskey = false; - foreach (array('PROTEINID', 'CRYSTALID') as $f) { - if ($s) { - if (array_key_exists($f, $s)) { - $a[$f] = $s[$f]; - $haskey = true; - } - } else { - if ($this->has_arg($f)) { - $a[$f] = $this->arg($f); - $haskey = true; - } - } - } - if (!$haskey) $this->_error('One or more fields is missing'); + $this->db->end_transaction(); + $this->_output($col); + } - foreach (array('COMMENTS', 'SPACEGROUP', 'CODE', 'ANOMALOUSSCATTERER') as $f) { - if ($s) $a[$f] = array_key_exists($f, $s) ? $s[$f] : ''; - else $a[$f] = $this->has_arg($f) ? $this->arg($f) : ''; + function _prepare_sample_args($s = null) + { + $a = array(); + foreach (array('LOCATION', 'CONTAINERID', 'NAME') as $f) { + if ($s) { + if (!array_key_exists($f, $s)) + $this->_error('One or more fields are mising'); + else + $a[$f] = $s[$f]; + } else { + if (!$this->has_arg($f)) + $this->_error('One or more fields are mising'); + else + $a[$f] = $this->arg($f); } + } - foreach ( - array( - 'CENTRINGMETHOD', - 'EXPERIMENTKIND', - 'RADIATIONSENSITIVITY', - 'SCREENCOMPONENTGROUPID', - 'BLSUBSAMPLEID', - 'COMPONENTIDS', - 'COMPONENTAMOUNTS', - 'REQUIREDRESOLUTION', - 'CELL_A', - 'CELL_B', - 'CELL_C', - 'CELL_ALPHA', - 'CELL_BETA', - 'CELL_GAMMA', - 'VOLUME', - 'ABUNDANCE', - 'PACKINGFRACTION', - 'DIMENSION1', - 'DIMENSION2', - 'DIMENSION3', - 'SHAPE', - 'THEORETICALDENSITY', - 'LOOPTYPE', - 'ENERGY', - 'USERPATH', - 'SCREENINGMETHOD', - 'SCREENINGCOLLECTVALUE', - 'SAMPLEGROUP', - 'STRATEGYOPTION', - 'MINIMUMRESOLUTION', - 'INITIALSAMPLEGROUP' - ) as $f - ) { - if ($s) $a[$f] = array_key_exists($f, $s) ? $s[$f] : null; - else $a[$f] = $this->has_arg($f) ? $this->arg($f) : null; + $haskey = false; + foreach (array('PROTEINID', 'CRYSTALID') as $f) { + if ($s) { + if (array_key_exists($f, $s)) { + $a[$f] = $s[$f]; + $haskey = true; + } + } else { + if ($this->has_arg($f)) { + $a[$f] = $this->arg($f); + $haskey = true; + } } + } + if (!$haskey) + $this->_error('One or more fields is missing'); + - return $a; + foreach (array('COMMENTS', 'SPACEGROUP', 'CODE', 'ANOMALOUSSCATTERER') as $f) { + if ($s) + $a[$f] = array_key_exists($f, $s) ? $s[$f] : ''; + else + $a[$f] = $this->has_arg($f) ? $this->arg($f) : ''; } + foreach (array( + 'CENTRINGMETHOD', + 'EXPERIMENTKIND', + 'RADIATIONSENSITIVITY', + 'SCREENCOMPONENTGROUPID', + 'BLSUBSAMPLEID', + 'COMPONENTIDS', + 'COMPONENTAMOUNTS', + 'REQUIREDRESOLUTION', + 'CELL_A', + 'CELL_B', + 'CELL_C', + 'CELL_ALPHA', + 'CELL_BETA', + 'CELL_GAMMA', + 'VOLUME', + 'ABUNDANCE', + 'PACKINGFRACTION', + 'DIMENSION1', + 'DIMENSION2', + 'DIMENSION3', + 'SHAPE', + 'COLOR', + 'THEORETICALDENSITY', + 'LOOPTYPE', + 'ENERGY', + 'USERPATH', + 'SCREENINGMETHOD', + 'SCREENINGCOLLECTVALUE', + 'SAMPLEGROUP', + 'STRATEGYOPTION', + 'MINIMUMRESOLUTION', + 'INITIALSAMPLEGROUP' + ) as $f) { + if ($s) + $a[$f] = array_key_exists($f, $s) ? $s[$f] : null; + else + $a[$f] = $this->has_arg($f) ? $this->arg($f) : null; + } + + return $a; + } - function _do_add_sample($s) { - $a = $this->_prepare_strategy_option_for_sample($s); - $this->db->pq("INSERT INTO diffractionplan (diffractionplanid, requiredresolution, anomalousscatterer, centringmethod, experimentkind, radiationsensitivity, energy, userpath, strategyoption, minimalresolution) VALUES (s_diffractionplan.nextval, :1, :2, :3, :4, :5, :6, :7, :8, :9) RETURNING diffractionplanid INTO :id", - array($a['REQUIREDRESOLUTION'], $a['ANOMALOUSSCATTERER'], $a['CENTRINGMETHOD'], $a['EXPERIMENTKIND'], $a['RADIATIONSENSITIVITY'], $a['ENERGY'], $a['USERPATH'], $a['STRATEGYOPTION'], $a['MINIMUMRESOLUTION'])); - $did = $this->db->id(); + function _do_add_sample($s) + { + $a = $this->_prepare_strategy_option_for_sample($s); + + $this->db->pq( + "INSERT INTO diffractionplan (diffractionplanid, requiredresolution, anomalousscatterer, centringmethod, experimentkind, radiationsensitivity, energy, userpath, strategyoption, minimalresolution) VALUES (s_diffractionplan.nextval, :1, :2, :3, :4, :5, :6, :7, :8, :9) RETURNING diffractionplanid INTO :id", + array($a['REQUIREDRESOLUTION'], $a['ANOMALOUSSCATTERER'], $a['CENTRINGMETHOD'], $a['EXPERIMENTKIND'], $a['RADIATIONSENSITIVITY'], $a['ENERGY'], $a['USERPATH'], $a['STRATEGYOPTION'], $a['MINIMUMRESOLUTION']) + ); + $did = $this->db->id(); - if (!array_key_exists('CRYSTALID', $a)) { - $chk = $this->db->pq("SELECT pr.proteinid + if (!array_key_exists('CRYSTALID', $a)) { + $chk = $this->db->pq("SELECT pr.proteinid FROM protein pr INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE p.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid, $a['PROTEINID'])); - if (!sizeof($chk)) $this->_error('No such crystal'); + if (!sizeof($chk)) + $this->_error('No such crystal'); - $this->db->pq("INSERT INTO crystal (crystalid,proteinid,spacegroup,cell_a,cell_b,cell_c,cell_alpha,cell_beta,cell_gamma,abundance,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10) RETURNING crystalid INTO :id", - array($a['PROTEINID'], $a['SPACEGROUP'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['ABUNDANCE'], $a['THEORETICALDENSITY'])); - $crysid = $this->db->id(); + $this->db->pq( + "INSERT INTO crystal (crystalid,proteinid,spacegroup,cell_a,cell_b,cell_c,cell_alpha,cell_beta,cell_gamma,abundance,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10) RETURNING crystalid INTO :id", + array($a['PROTEINID'], $a['SPACEGROUP'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['ABUNDANCE'], $a['THEORETICALDENSITY']) + ); + $crysid = $this->db->id(); - if ($a['COMPONENTIDS']) $this->_update_sample_components(array(), $a['COMPONENTIDS'], $a['COMPONENTAMOUNTS'], $crysid); - } else { - $chk = $this->db->pq("SELECT cr.crystalid + if ($a['COMPONENTIDS']) + $this->_update_sample_components(array(), $a['COMPONENTIDS'], $a['COMPONENTAMOUNTS'], $crysid); + } else { + $chk = $this->db->pq("SELECT cr.crystalid FROM crystal cr INNER JOIN protein pr ON pr.proteinid = cr.proteinid INNER JOIN proposal p ON p.proposalid = pr.proposalid WHERE p.proposalid = :1 AND cr.crystalid = :2", array($this->proposalid, $a['CRYSTALID'])); - if (!sizeof($chk)) $this->_error('No such crystal'); + if (!sizeof($chk)) + $this->_error('No such crystal'); - $crysid = $a['CRYSTALID']; - } + $crysid = $a['CRYSTALID']; + } - $this->db->pq("INSERT INTO blsample (blsampleid,crystalid,diffractionplanid,containerid,location,comments,name,code,blsubsampleid,screencomponentgroupid,volume,packingfraction,dimension1,dimension2,dimension3,shape,looptype) VALUES (s_blsample.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16) RETURNING blsampleid INTO :id", - array($crysid, $did, $a['CONTAINERID'], $a['LOCATION'], $a['COMMENTS'], $a['NAME'] ,$a['CODE'], $a['BLSUBSAMPLEID'], $a['SCREENCOMPONENTGROUPID'], $a['VOLUME'], $a['PACKINGFRACTION'], $a['DIMENSION1'], $a['DIMENSION2'], $a['DIMENSION3'],$a['SHAPE'],$a['LOOPTYPE'])); - $sid = $this->db->id(); + $this->db->pq( + "INSERT INTO blsample (blsampleid,crystalid,diffractionplanid,containerid,location,comments,name,code,blsubsampleid,screencomponentgroupid,volume,packingfraction,dimension1,dimension2,dimension3,shape,looptype) VALUES (s_blsample.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15,:16) RETURNING blsampleid INTO :id", + array($crysid, $did, $a['CONTAINERID'], $a['LOCATION'], $a['COMMENTS'], $a['NAME'], $a['CODE'], $a['BLSUBSAMPLEID'], $a['SCREENCOMPONENTGROUPID'], $a['VOLUME'], $a['PACKINGFRACTION'], $a['DIMENSION1'], $a['DIMENSION2'], $a['DIMENSION3'], $a['SHAPE'], $a['LOOPTYPE']) + ); + $sid = $this->db->id(); - if ($a['VALID_SAMPLE_GROUP']) { - $this->_save_sample_to_group($sid, $a['SAMPLEGROUP'], null, null); - } + if ($a['VALID_SAMPLE_GROUP']) { + $this->_save_sample_to_group($sid, $a['SAMPLEGROUP'], null, null); + } + + return $sid; + } + + # Move Container + function _move_sample_to_another_container() + { + if (!$this->has_arg('CONTAINERID')) + $this->_error('No container specified'); + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); + if (!$this->has_arg('LOCATION')) + $this->_error('No location specified'); + + $check_container = $this->db->pq( + "SELECT c.containerid + FROM container c + INNER JOIN dewar d ON c.dewarid = d.dewarid + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN proposal p ON p.proposalid = s.proposalid + WHERE c.containerid=:1 AND p.proposalid=:2", + array($this->arg('CONTAINERID'), $this->proposalid) + ); - return $sid; + if (sizeof($check_container) < 1) { + $this->_error('This container does not exist'); } - function _prepare_strategy_option_for_sample($a) { - $is_valid_sample_group = false; - $strategyOptionsData = ["sample_group" => null]; + $check_sample = $this->db->pq( + "SELECT bls.blsampleid + FROM blsample bls + INNER JOIN container c ON bls.containerid = c.containerid + INNER JOIN dewar d ON c.dewarid = d.dewarid + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN proposal p ON p.proposalid = s.proposalid + WHERE bls.blsampleid=:1 AND p.proposalid=:2", + array($this->arg('BLSAMPLEID'), $this->proposalid) + ); - if (isset($a['SAMPLEGROUP'])) { - $args = array($this->proposalid); - array_push($args, $a['SAMPLEGROUP']); - $check = $this->db->pq("SELECT blsamplegroupid FROM blsamplegroup WHERE proposalid = :1 AND blsamplegroupid = :2", $args); + if (sizeof($check_sample) < 1) { + $this->_error('Sample does not exist'); + } - if (sizeof($check)) { - $is_valid_sample_group = true; - $strategyOptionsData["sample_group"] = $a["SAMPLEGROUP"]; - } - } + $container_samples = $this->db->pq("SELECT s.blsampleid, s.location FROM blsample s WHERE s.containerid=:1", array($this->arg('CONTAINERID'))); + $filled_locations = array_map(function ($item) { + return $item['LOCATION']; + }, $container_samples); - if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'best' && $is_valid_sample_group) { - $strategyOptionsData = array_merge($strategyOptionsData, array( - "screen" => $a['SCREENINGMETHOD'], - "collect_samples" => intval($a['SCREENINGCOLLECTVALUE']), - )); + if (in_array($this->arg('LOCATION'), $filled_locations)) { + $this->_error('Specified location already has sample in it'); + } - $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); - } - else if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'all') { - $strategyOptionsData = array_merge($strategyOptionsData, array( - "screen" => $a['SCREENINGMETHOD'], - "collect_samples" => null - )); + $this->db->pq( + "UPDATE blsample SET containerid=:1, location=:2 WHERE blsampleid=:3", + array($this->arg('CONTAINERID'), $this->arg('LOCATION'), $this->arg('BLSAMPLEID')) + ); + $this->_output(array('message' => 'Sample moved successfully')); + } - $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); - } - else if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'none') { - $strategyOptionsData = array_merge($strategyOptionsData, array( - "screen" => null, - "collect_samples" => null - )); - $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); - } else { - $a['STRATEGYOPTION'] = null; + function _prepare_strategy_option_for_sample($a) + { + $is_valid_sample_group = false; + $strategyOptionsData = ["sample_group" => null]; + + if (isset($a['SAMPLEGROUP'])) { + $args = array($this->proposalid); + array_push($args, $a['SAMPLEGROUP']); + $check = $this->db->pq("SELECT blsamplegroupid FROM blsamplegroup WHERE proposalid = :1 AND blsamplegroupid = :2", $args); + + if (sizeof($check)) { + $is_valid_sample_group = true; + $strategyOptionsData["sample_group"] = $a["SAMPLEGROUP"]; } + } + + if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'best' && $is_valid_sample_group) { + $strategyOptionsData = array_merge($strategyOptionsData, array( + "screen" => $a['SCREENINGMETHOD'], + "collect_samples" => intval($a['SCREENINGCOLLECTVALUE']), + )); + + $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); + } else if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'all') { + $strategyOptionsData = array_merge($strategyOptionsData, array( + "screen" => $a['SCREENINGMETHOD'], + "collect_samples" => null + )); - $a['VALID_SAMPLE_GROUP'] = $is_valid_sample_group; + $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); + } else if (isset($a["SCREENINGMETHOD"]) && $a['SCREENINGMETHOD'] == 'none') { + $strategyOptionsData = array_merge($strategyOptionsData, array( + "screen" => null, + "collect_samples" => null + )); - return $a; + $a['STRATEGYOPTION'] = json_encode($strategyOptionsData); + } else { + $a['STRATEGYOPTION'] = null; } - - # ------------------------------------------------------------------------ - # List of proteins for a proposal - function _proteins() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + $a['VALID_SAMPLE_GROUP'] = $is_valid_sample_group; - $args = array($this->proposalid); - $where = '(pr.proposalid=:1 /*or pr.global=1*/)'; - $join = ''; - $extc = ''; + return $a; + } - if ($this->has_arg('pjid')) { - $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personid, $this->user->personid)); - if (!sizeof($info)) $this->_error('No such project'); - $args = array($this->arg('pjid')); - $where = 'pj.projectid=:'.sizeof($args); - $join .= ' INNER JOIN project_has_protein pj ON pj.proteinid=pr.proteinid'; - - if (!$this->staff) { - $join .= " INNER JOIN blsession ses ON ses.proposalid = p.proposalid - INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:".(sizeof($args)+1); + # ------------------------------------------------------------------------ + # List of proteins for a proposal + function _proteins() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - array_push($args, $this->user->personid); - } - } - - if ($this->has_arg('pid')) { - $where .= ' AND pr.proteinid=:'.(sizeof($args)+1); - array_push($args, $this->arg('pid')); - $extc = 'pr.sequence, '; - } + $args = array($this->proposalid); + $where = '(pr.proposalid=:1 /*or pr.global=1*/)'; + $join = ''; + $extc = ''; - if ($this->has_arg('seq')) { - $extc = 'pr.sequence, '; - } + if ($this->has_arg('pjid')) { + $info = $this->db->pq('SELECT p.title FROM project p LEFT OUTER JOIN project_has_person php ON php.projectid = p.projectid WHERE p.projectid=:1 AND (p.personid=:2 or php.personid=:3)', array($this->arg('pjid'), $this->user->personId, $this->user->personId)); + if (!sizeof($info)) + $this->_error('No such project'); + $args = array($this->arg('pjid')); + $where = 'pj.projectid=:' . sizeof($args); + $join .= ' INNER JOIN project_has_protein pj ON pj.proteinid=pr.proteinid'; - if ($this->has_arg('type')) { - $where .= ' AND pr.componenttypeid=:'.(sizeof($args)+1); - array_push($args, $this->arg('type')); - } + if (!$this->staff) { + $join .= " INNER JOIN blsession ses ON ses.proposalid = p.proposalid + INNER JOIN session_has_person shp ON shp.sessionid = ses.sessionid AND shp.personid=:" . (sizeof($args) + 1); - if ($this->has_arg('external')) { - $where .= ' AND pr.externalid IS NOT NULL'; + array_push($args, $this->user->personId); } - - if ($this->has_arg('SAFETYLEVEL')) { - $where .= ' AND pr.safetylevel=:'.(sizeof($args)+1); - array_push($args, $this->arg('SAFETYLEVEL')); - } - + } - $tot = $this->db->pq("SELECT count(distinct pr.proteinid) as tot FROM protein pr INNER JOIN proposal p ON p.proposalid = pr.proposalid $join WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + if ($this->has_arg('pid')) { + $where .= ' AND pr.proteinid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('pid')); + $extc = 'pr.sequence, '; + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(pr.name) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')))"; - for ($i = 0; $i < 2; $i++) array_push($args, $this->arg('s')); - } - - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'pr.proteinid DESC'; + if ($this->has_arg('seq')) { + $extc = 'pr.sequence, '; + } - $group = 'pr.proteinId'; - // Only display original UAS approved proteins (not clones which have the same externalId) - if($this->has_arg('original') && $this->arg('original') == 1){ - $group = 'pr.externalId'; - $order .= ', pr.bltimeStamp DESC'; - } + if ($this->has_arg('type')) { + $where .= ' AND pr.componenttypeid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('type')); + } - if ($this->has_arg('sort_by')) { - $cols = array('NAME' => 'pr.name', 'ACRONYM' => 'pr.acronym', 'MOLECULARMASS' =>'pr.molecularmass', 'HASSEQ' => "CASE WHEN sequence IS NULL THEN 'No' ELSE 'Yes' END"); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $rows = $this->db->paginate("SELECT /*distinct*/ $extc pr.concentrationtypeid, + if ($this->has_arg('external')) { + $where .= ' AND pr.externalid IS NOT NULL'; + } + + if ($this->has_arg('SAFETYLEVEL')) { + $where .= ' AND pr.safetylevel=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SAFETYLEVEL')); + } + + + $tot = $this->db->pq("SELECT count(distinct pr.proteinid) as tot FROM protein pr INNER JOIN proposal p ON p.proposalid = pr.proposalid $join WHERE $where", $args); + $tot = intval($tot[0]['TOT']); + + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(pr.name) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')))"; + for ($i = 0; $i < 2; $i++) + array_push($args, $this->arg('s')); + } + + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'pr.proteinid DESC'; + + $group = 'pr.proteinId'; + + // Only display original UAS approved proteins (not clones which have the same externalId) + if ($this->has_arg('original') && $this->arg('original') == 1) { + $group = 'pr.externalId'; + $order .= ', pr.bltimeStamp DESC'; + } + + if ($this->has_arg('sort_by')) { + $cols = array('NAME' => 'pr.name', 'ACRONYM' => 'pr.acronym', 'MOLECULARMASS' => 'pr.molecularmass', 'HASSEQ' => "CASE WHEN sequence IS NULL THEN 'No' ELSE 'Yes' END"); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + + $rows = $this->db->paginate("SELECT /*distinct*/ $extc pr.concentrationtypeid, ct.symbol as concentrationtype, pr.componenttypeid, cmt.name as componenttype, CASE WHEN sequence IS NULL THEN 'No' ELSE 'Yes' END as hasseq, pr.proteinid, @@ -1553,81 +1750,88 @@ function _proteins() { WHERE $where GROUP BY $group ORDER BY $order", $args); - - $ids = array(); - $wcs = array(); - foreach ($rows as $r) { - array_push($ids, $r['PROTEINID']); - array_push($wcs, 'pr.proteinid=:'.sizeof($ids)); - } - - $dcs = array(); - $scs = array(); - - if (sizeof($ids)) { - $dcst = $this->db->pq('SELECT pr.proteinid, count(dc.datacollectionid) as dcount FROM datacollection dc INNER JOIN blsample s ON s.blsampleid=dc.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid WHERE '.implode(' OR ', $wcs).' GROUP BY pr.proteinid', $ids); - - foreach ($dcst as $d) { - $dcs[$d['PROTEINID']] = $d['DCOUNT']; - } + $ids = array(); + $wcs = array(); + foreach ($rows as $r) { + array_push($ids, $r['PROTEINID']); + array_push($wcs, 'pr.proteinid=:' . sizeof($ids)); + } - $scst = $this->db->pq('SELECT pr.proteinid, count(s.blsampleid) as scount FROM blsample s INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid WHERE '.implode(' OR ', $wcs).' GROUP BY pr.proteinid', $ids); + $dcs = array(); + $scs = array(); - foreach ($scst as $d) { - $scs[$d['PROTEINID']] = $d['SCOUNT']; - } + if (sizeof($ids)) { + $dcst = $this->db->pq('SELECT pr.proteinid, count(dc.datacollectionid) as dcount FROM datacollection dc INNER JOIN blsample s ON s.blsampleid=dc.blsampleid INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid WHERE ' . implode(' OR ', $wcs) . ' GROUP BY pr.proteinid', $ids); + + + foreach ($dcst as $d) { + $dcs[$d['PROTEINID']] = $d['DCOUNT']; } - - foreach ($rows as &$r) { - $dcount = array_key_exists($r['PROTEINID'], $dcs) ? $dcs[$r['PROTEINID']] : 0; - $r['DCOUNT'] = $dcount; - $scount = array_key_exists($r['PROTEINID'], $scs) ? $scs[$r['PROTEINID']] : 0; - $r['SCOUNT'] = $scount; - - if ($this->has_arg('pid')) $r['SEQUENCE'] = $this->db->read($r['SEQUENCE']); + + $scst = $this->db->pq('SELECT pr.proteinid, count(s.blsampleid) as scount FROM blsample s INNER JOIN crystal cr ON cr.crystalid = s.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid WHERE ' . implode(' OR ', $wcs) . ' GROUP BY pr.proteinid', $ids); + + foreach ($scst as $d) { + $scs[$d['PROTEINID']] = $d['SCOUNT']; } - - if ($this->has_arg('pid')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such protein'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); } - - # ------------------------------------------------------------------------ - # Return distinct proteins for a proposal - function _disinct_proteins() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $args = array($this->proposalid); - $where = '(pr.proposalid=:1)'; + foreach ($rows as &$r) { + $dcount = array_key_exists($r['PROTEINID'], $dcs) ? $dcs[$r['PROTEINID']] : 0; + $r['DCOUNT'] = $dcount; + $scount = array_key_exists($r['PROTEINID'], $scs) ? $scs[$r['PROTEINID']] : 0; + $r['SCOUNT'] = $scount; - if ($this->has_arg('global')) { - $where = '(pr.proposalid=:1 OR pr.global=1)'; - } + if ($this->has_arg('pid')) + $r['SEQUENCE'] = $this->db->read($r['SEQUENCE']); + } - if ($this->has_arg('external')) { - $where .= ' AND pr.externalid IS NOT NULL'; - } + if ($this->has_arg('pid')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such protein'); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } - $has_safety_level = $this->has_arg('SAFETYLEVEL'); - if ($has_safety_level && $this->arg('SAFETYLEVEL') === 'ALL') { - $where .= ' AND pr.safetyLevel IS NOT NULL'; - } else if ($has_safety_level && $this->arg('SAFETYLEVEL') !== 'ALL') { - $where .= ' AND pr.safetyLevel=:'.(sizeof($args)+1); - array_push($args, $this->arg('SAFETYLEVEL')); - } - if ($this->has_arg('term')) { - $where .= " AND (lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:".(sizeof($args)+1)."), '%')) OR lower(pr.name) LIKE lower(CONCAT(CONCAT('%',:".(sizeof($args)+2)."), '%')))"; - array_push($args, $this->arg('term')); - array_push($args, $this->arg('term')); - } + # ------------------------------------------------------------------------ + # Return distinct proteins for a proposal + function _disinct_proteins() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + $args = array($this->proposalid); + $where = '(pr.proposalid=:1)'; - $rows = $this->db->pq("SELECT distinct pr.global, pr.name, pr.acronym, pr.safetylevel, + if ($this->has_arg('global')) { + $where = '(pr.proposalid=:1 OR pr.global=1)'; + } + + if ($this->has_arg('external')) { + $where .= ' AND pr.externalid IS NOT NULL'; + } + + $has_safety_level = $this->has_arg('SAFETYLEVEL'); + if ($has_safety_level && $this->arg('SAFETYLEVEL') === 'ALL') { + $where .= ' AND pr.safetyLevel IS NOT NULL'; + } else if ($has_safety_level && $this->arg('SAFETYLEVEL') !== 'ALL') { + $where .= ' AND pr.safetyLevel=:' . (sizeof($args) + 1); + array_push($args, $this->arg('SAFETYLEVEL')); + } + + if ($this->has_arg('term')) { + $where .= " AND (lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:" . (sizeof($args) + 1) . "), '%')) OR lower(pr.name) LIKE lower(CONCAT(CONCAT('%',:" . (sizeof($args) + 2) . "), '%')))"; + array_push($args, $this->arg('term')); + array_push($args, $this->arg('term')); + } + + $rows = $this->db->pq("SELECT distinct pr.global, pr.name, pr.acronym, pr.safetylevel, max(pr.proteinid) as proteinid, ct.symbol as concentrationtype, 1 as hasph, @@ -1637,469 +1841,528 @@ function _disinct_proteins() { WHERE pr.acronym is not null AND $where GROUP BY ct.symbol, pr.acronym, pr.name, pr.global ORDER BY lower(pr.acronym)", $args); - - $this->_output($rows); - } + $this->_output($rows); + } - - # ------------------------------------------------------------------------ - # Update a particular field for a protein - function _update_protein() { - if (!$this->has_arg('pid')) $this->_error('No proteinid specified'); - $prot = $this->db->pq("SELECT pr.proteinid, pr.sequence FROM protein pr - WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid,$this->arg('pid'))); - - if (!sizeof($prot)) $this->_error('No such protein'); - - foreach(array('NAME', 'SEQUENCE', 'ACRONYM', 'MOLECULARMASS', 'CONCENTRATIONTYPEID', 'COMPONENTTYPEID', 'GLOBAL', 'DENSITY') as $f) { - if ($this->has_arg($f)) { - $this->db->pq('UPDATE protein SET '.$f.'=:1 WHERE proteinid=:2', array($this->arg($f), $this->arg('pid'))); - $this->_output(array($f => $this->arg($f))); - } - } - if ($this->has_arg('SEQUENCE') && empty($prot[0]['SEQUENCE'])) { - # Only trigger alphafold if the sequence was previously empty - $this->_on_add_protein_sequence($prot[0]["PROTEINID"]); + # ------------------------------------------------------------------------ + # Update a particular field for a protein + function _update_protein() + { + if (!$this->has_arg('pid')) + $this->_error('No proteinid specified'); + + $prot = $this->db->pq("SELECT pr.proteinid, pr.sequence FROM protein pr + WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid, $this->arg('pid'))); + + if (!sizeof($prot)) + $this->_error('No such protein'); + + if ($this->has_arg('ACRONYM')) { + $chk = $this->db->pq("SELECT pr.proteinid FROM protein pr + WHERE pr.proposalid = :1 AND pr.acronym = :2", array($this->proposalid, $this->arg('ACRONYM'))); + if (sizeof($chk)) $this->_error('That protein acronym already exists in this proposal'); + } + + foreach (array('NAME', 'SEQUENCE', 'ACRONYM', 'MOLECULARMASS', 'CONCENTRATIONTYPEID', 'COMPONENTTYPEID', 'GLOBAL', 'DENSITY') as $f) { + if ($this->has_arg($f)) { + $this->db->pq('UPDATE protein SET ' . $f . '=:1 WHERE proteinid=:2', array($this->arg($f), $this->arg('pid'))); + $this->_output(array($f => $this->arg($f))); } } + if ($this->has_arg('SEQUENCE') && empty($prot[0]['SEQUENCE'])) { + # Only trigger alphafold if the sequence was previously empty + $this->_on_add_protein_sequence($prot[0]["PROTEINID"]); + } + } + - # ------------------------------------------------------------------------ - # Update a particular field for a sample - function _update_sample() { + # ------------------------------------------------------------------------ + # Update a particular field for a sample + function _update_sample() + { - if (!$this->has_arg('sid')) $this->_error('No sampleid specified'); + if (!$this->has_arg('sid')) + $this->_error('No sampleid specified'); - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid + $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid FROM blsample b INNER JOIN crystal cr ON cr.crystalid = b.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid - WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid,$this->arg('sid'))); + WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $this->arg('sid'))); - if (!sizeof($samp)) $this->_error('No such sample'); - else $samp = $samp[0]; + if (!sizeof($samp)) + $this->_error('No such sample'); + else + $samp = $samp[0]; - if($this->has_arg('CONTAINERID') && $this->arg('CONTAINERID') == 0) { - $defaultContainerLocation = $this->_get_default_sample_container(); - $this->args['CONTAINERID'] = $defaultContainerLocation['CONTAINERID']; - $this->args['LOCATION'] = $defaultContainerLocation['LOCATION']; - } - - $maxLocation = $this->_get_current_max_dcp_plan_order($this->args['CONTAINERID']); - $maxLocation = sizeof($maxLocation) ? $maxLocation : -1; + if ($this->has_arg('CONTAINERID') && $this->arg('CONTAINERID') == 0) { + $defaultContainerLocation = $this->_get_default_sample_container(); + $this->args['CONTAINERID'] = $defaultContainerLocation['CONTAINERID']; + $this->args['LOCATION'] = $defaultContainerLocation['LOCATION']; + } - $sfields = array('CODE', 'NAME', 'COMMENTS', 'VOLUME', 'PACKINGFRACTION', 'DIMENSION1', 'DIMENSION2', 'DIMENSION3', 'SHAPE', 'POSITION', 'CONTAINERID', 'LOOPTYPE', 'LOCATION'); - foreach ($sfields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE blsample SET $f=:1 WHERE blsampleid=:2", array($this->arg($f), $samp['BLSAMPLEID'])); - $this->_output(array($f => $this->arg($f))); - } + $sfields = array('CODE', 'NAME', 'COMMENTS', 'VOLUME', 'PACKINGFRACTION', 'DIMENSION1', 'DIMENSION2', 'DIMENSION3', 'SHAPE', 'POSITION', 'CONTAINERID', 'LOOPTYPE', 'LOCATION'); + foreach ($sfields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE blsample SET $f=:1 WHERE blsampleid=:2", array($this->arg($f), $samp['BLSAMPLEID'])); + $this->_output(array($f => $this->arg($f))); } + } - $cfields = array('PROTEINID', 'SPACEGROUP', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'THEORETICALDENSITY'); - foreach ($cfields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE crystal SET $f=:1 WHERE crystalid=:2", array($this->arg($f), $samp['CRYSTALID'])); - - if ($f == 'PROTEINID') { - $name = $this->db->pq('SELECT acronym FROM protein WHERE proteinid=:1', array($this->arg('PROTEINID'))); - if (sizeof($name)) { - $this->_output(array('ACRONYM' => $name[0]['ACRONYM'])); - } - } else $this->_output(array($f => $this->arg($f))); - } - } + $cfields = array('PROTEINID', 'SPACEGROUP', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'THEORETICALDENSITY', 'COLOR'); + foreach ($cfields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE crystal SET $f=:1 WHERE crystalid=:2", array($this->arg($f), $samp['CRYSTALID'])); - $dfields = array('REQUIREDRESOLUTION', 'ANOMALOUSSCATTERER', 'CENTRINGMETHOD', 'EXPERIMENTKIND', 'RADIATIONSENSITIVITY', 'ENERGY', 'USERPATH'); - foreach ($dfields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE diffractionplan SET $f=:1 WHERE diffractionplanid=:2", array($this->arg($f), $samp['DIFFRACTIONPLANID'])); + if ($f == 'PROTEINID') { + $name = $this->db->pq('SELECT acronym FROM protein WHERE proteinid=:1', array($this->arg('PROTEINID'))); + if (sizeof($name)) { + $this->_output(array('ACRONYM' => $name[0]['ACRONYM'])); + } + } else $this->_output(array($f => $this->arg($f))); - } } + } - if ($this->has_arg('SCREENINGMETHOD')) { - $sample_data = array( - "SCREENINGMETHOD" => $this->arg("SCREENINGMETHOD"), - "SCREENINGCOLLECTVALUE" => $this->arg("SCREENINGCOLLECTVALUE"), - "SAMPLEGROUP" => $this->arg("SAMPLEGROUP") - ); - $strategyOptionsData = $this->_prepare_strategy_option_for_sample($sample_data); - $this->db->pq("UPDATE diffractionplan SET STRATEGYOPTION = :1 WHERE diffractionplanid = :2", array($strategyOptionsData, $samp['DIFFRACTIONPLANID'])); + $dfields = array('REQUIREDRESOLUTION', 'ANOMALOUSSCATTERER', 'CENTRINGMETHOD', 'EXPERIMENTKIND', 'RADIATIONSENSITIVITY', 'ENERGY', 'USERPATH'); + foreach ($dfields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE diffractionplan SET $f=:1 WHERE diffractionplanid=:2", array($this->arg($f), $samp['DIFFRACTIONPLANID'])); $this->_output(array($f => $this->arg($f))); } + } + + if ($this->has_arg('SCREENINGMETHOD')) { + $sample_data = array( + "SCREENINGMETHOD" => $this->arg("SCREENINGMETHOD"), + "SCREENINGCOLLECTVALUE" => $this->arg("SCREENINGCOLLECTVALUE"), + "SAMPLEGROUP" => $this->arg("SAMPLEGROUP") + ); + $strategyOptionsData = $this->_prepare_strategy_option_for_sample($sample_data); + $this->db->pq("UPDATE diffractionplan SET STRATEGYOPTION = :1 WHERE diffractionplanid = :2", array($strategyOptionsData, $samp['DIFFRACTIONPLANID'])); + $this->_output(array($f => $this->arg($f))); + } - if($this->has_arg('PLANORDER')) { - // If we're moving a BLSample to a new container we need to adjust the DCP plan order not to clash with existing plans for samples in the new container - $dcps = $this->db->pq("SELECT dataCollectionPlanId FROM BLSample_has_DataCollectionPlan + if ($this->has_arg('PLANORDER')) { + // If we're moving a BLSample to a new container we need to adjust the DCP plan order not to clash with existing plans for samples in the new container + $dcps = $this->db->pq("SELECT dataCollectionPlanId FROM BLSample_has_DataCollectionPlan WHERE blSampleId = :1", array($this->arg('sid'))); - if(sizeof($dcps)) { - foreach($dcps as $dcp){ - ++$maxLocation; - $this->db->pq("UPDATE BLSample_has_DataCollectionPlan SET planOrder = :1 WHERE dataCollectionPlanId = :2 AND blSampleId = :3", array($maxLocation, $dcp['DATACOLLECTIONPLANID'], $this->arg('sid'))); - } + if(sizeof($dcps)) { + $nextDCPIndex = 0; + if ($this->has_arg('CONTAINERID')) { + $nextDCPIndex = $this->_next_dcp_plan_order_index($this->args['CONTAINERID']); + } + + foreach($dcps as $dcp){ + $this->db->pq("UPDATE BLSample_has_DataCollectionPlan SET planOrder = :1 WHERE dataCollectionPlanId = :2 AND blSampleId = :3", array($nextDCPIndex, $dcp['DATACOLLECTIONPLANID'], $this->arg('sid'))); + $nextDCPIndex++; } } } + } - # ------------------------------------------------------------------------ - # Look up the default container for proposal and the next available location - function _get_default_sample_container() { + # ------------------------------------------------------------------------ + # Look up the default container for proposal and the next available location + function _get_default_sample_container() + { - $containers = $this->db->pq("SELECT DISTINCT c.containerId, c.code, b.location + $containers = $this->db->pq("SELECT DISTINCT c.containerId, c.code, b.location FROM Container c INNER JOIN BLSample b ON b.containerId = c.containerId INNER JOIN Crystal cr ON cr.crystalId = b.crystalId INNER JOIN Protein pr ON pr.proteinId = cr.proteinId WHERE pr.proposalId =:1 AND c.code LIKE :2", array($this->proposalid, "%_samples")); - $free = null; + $free = null; - if(sizeof($containers)){ - $locations = array(); - foreach($containers as $c) { - array_push($locations, $c['LOCATION']); - } + if (sizeof($containers)) { + $locations = array(); + foreach ($containers as $c) { + array_push($locations, $c['LOCATION']); + } - $free = max($locations)+1; + $free = max($locations) + 1; - for($i=1; $idb->pq("SELECT c.containerId, c.code + } + } else { + // It's possible the default container is empty so we would fail to find it above + // Find it via dewars and shipping instead + $containers = $this->db->pq("SELECT c.containerId, c.code FROM Container c INNER JOIN Dewar d ON c.dewarId = d.dewarId INNER JOIN Shipping s ON d.shippingId = s.shippingId INNER JOIN Proposal p on s.proposalId = p.proposalId WHERE p.proposalId = :1 AND c.code LIKE :2", array($this->proposalid, "%_samples")); - $free = 1; - } - - return array('CONTAINERID'=>$containers[0]['CONTAINERID'], 'LOCATION'=>$free); + $free = 1; } + return array('CONTAINERID' => $containers[0]['CONTAINERID'], 'LOCATION' => $free); + } + - # ------------------------------------------------------------------------ - # Look up highest value DPC plan order to append new ones - function _get_current_max_dcp_plan_order($containerId) { + # ------------------------------------------------------------------------ + # Look up highest value DPC plan order and adds 1 to it, index starts at 0 + function _next_dcp_plan_order_index($containerId) + { - $maxLocation = $this->db->pq("SELECT MAX(bhd.planOrder) AS LOC + $rows = $this->db->pq("SELECT MAX(bhd.planOrder) AS LOC FROM BLSample_has_DataCollectionPlan bhd INNER JOIN BLSample bls ON bls.blSampleId = bhd.blSampleId INNER JOIN Container c ON c.containerId = bls.containerId WHERE c.containerId = :1", array($containerId)); - return $maxLocation[0]['LOC']; + if (sizeof($rows) == 0) { + $maxLocation = 0; + } else { + $maxLocation = $rows[0]['LOC']; + $maxLocation = $maxLocation ? $maxLocation + 1 : 0; } - - # ------------------------------------------------------------------------ - # Get list of pdbs for a proposal - function _get_pdbs() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $where = 'pr.proposalid=:1'; - $args = array($this->proposalid); - - if ($this->has_arg('pid')) { - $where .= ' AND pr.proteinid=:2'; - array_push($args, $this->arg('pid')); - } + return $maxLocation; + } - $rows = $this->db->pq("SELECT distinct hp.proteinhaspdbid, p.pdbid,pr.proteinid, p.name,p.code FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE $where ORDER BY p.pdbid DESC", $args); - - $this->_output($rows); + + # ------------------------------------------------------------------------ + # Get list of pdbs for a proposal + function _get_pdbs() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + $where = 'pr.proposalid=:1'; + $args = array($this->proposalid); + + if ($this->has_arg('pid')) { + $where .= ' AND pr.proteinid=:2'; + array_push($args, $this->arg('pid')); } - - # ------------------------------------------------------------------------ - # Add a new pdb - function _add_pdb() { - if (!$this->has_arg('PROTEINID')) $this->_error('No protein id specified'); - $prot = $this->db->pq("SELECT pr.proteinid + $rows = $this->db->pq("SELECT distinct hp.proteinhaspdbid, p.pdbid,pr.proteinid, p.name,p.code FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE $where ORDER BY p.pdbid DESC", $args); + + $this->_output($rows); + } + + # ------------------------------------------------------------------------ + # Add a new pdb + function _add_pdb() + { + if (!$this->has_arg('PROTEINID')) + $this->_error('No protein id specified'); + + $prot = $this->db->pq("SELECT pr.proteinid FROM protein pr - WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid,$this->arg('PROTEINID'))); - - if (!sizeof($prot)) $this->_error('No such protein'); + WHERE pr.proposalid = :1 AND pr.proteinid = :2", array($this->proposalid, $this->arg('PROTEINID'))); - if (array_key_exists('pdb_file', $_FILES)) { - if ($_FILES['pdb_file']['name']) { - $info = pathinfo($_FILES['pdb_file']['name']); - - if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') { - $file = file_get_contents($_FILES['pdb_file']['tmp_name']); - $this->_associate_pdb($info['basename'],$file,'',$this->arg('PROTEINID')); - } - } - } - - if ($this->has_arg('pdb_code')) { - $this->_associate_pdb($this->arg('pdb_code'),'',$this->arg('pdb_code'),$this->arg('PROTEINID')); - } + if (!sizeof($prot)) + $this->_error('No such protein'); - if ($this->has_arg('existing_pdb')) { - $rows = $this->db->pq("SELECT p.pdbid FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE pr.proposalid=:1 AND p.pdbid=:2", array($this->proposalid, $this->arg('existing_pdb'))); - - if (!sizeof($rows)) $this->_error('The specified pdb doesnt exist'); - - $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($this->arg('PROTEINID'),$this->arg('existing_pdb'))); + if (array_key_exists('pdb_file', $_FILES)) { + if ($_FILES['pdb_file']['name']) { + $info = pathinfo($_FILES['pdb_file']['name']); + + if ($info['extension'] == 'pdb' || $info['extension'] == 'cif') { + $file = file_get_contents($_FILES['pdb_file']['tmp_name']); + $this->_associate_pdb($info['basename'], $file, '', $this->arg('PROTEINID')); + } } - - $this->_output(1); + } + if ($this->has_arg('pdb_code')) { + $this->_associate_pdb($this->arg('pdb_code'), '', $this->arg('pdb_code'), $this->arg('PROTEINID')); } - - # Duplication :( - function _associate_pdb($name,$contents,$code,$pid) { - $this->db->pq("INSERT INTO pdb (pdbid,name,contents,code) VALUES(s_pdb.nextval,:1,:2,:3) RETURNING pdbid INTO :id", array($name,$contents,$code)); - $pdbid = $this->db->id(); - - $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($pid,$pdbid)); + + if ($this->has_arg('existing_pdb')) { + $rows = $this->db->pq("SELECT p.pdbid FROM pdb p INNER JOIN protein_has_pdb hp ON p.pdbid = hp.pdbid INNER JOIN protein pr ON pr.proteinid = hp.proteinid WHERE pr.proposalid=:1 AND p.pdbid=:2", array($this->proposalid, $this->arg('existing_pdb'))); + + if (!sizeof($rows)) + $this->_error('The specified pdb doesnt exist'); + + $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($this->arg('PROTEINID'), $this->arg('existing_pdb'))); } - - # ------------------------------------------------------------------------ - # Remove a pdb - function _remove_pdb() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - #if (!$this->has_arg('pid')) $this->_error('No protein specified'); - if (!$this->has_arg('pdbid')) $this->_error('No pdb specified'); - - $pdb = $this->db->pq("SELECT pd.pdbid + + $this->_output(1); + } + + # Duplication :( + function _associate_pdb($name, $contents, $code, $pid) + { + $this->db->pq("INSERT INTO pdb (pdbid,name,contents,code) VALUES(s_pdb.nextval,:1,:2,:3) RETURNING pdbid INTO :id", array($name, $contents, $code)); + $pdbid = $this->db->id(); + + $this->db->pq("INSERT INTO protein_has_pdb (proteinhaspdbid,proteinid,pdbid) VALUES (s_protein_has_pdb.nextval,:1,:2)", array($pid, $pdbid)); + } + + # ------------------------------------------------------------------------ + # Remove a pdb + function _remove_pdb() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + #if (!$this->has_arg('pid')) $this->_error('No protein specified'); + if (!$this->has_arg('pdbid')) + $this->_error('No pdb specified'); + + $pdb = $this->db->pq("SELECT pd.pdbid FROM pdb pd INNER JOIN protein_has_pdb hp ON hp.pdbid=pd.pdbid INNER JOIN protein p ON p.proteinid = hp.proteinid WHERE p.proposalid=:1 AND hp.proteinhaspdbid=:2", array($this->proposalid, $this->arg('pdbid'))); - - if (!sizeof($pdb)) $this->_error('No such pdb'); - else $pdb = $pdb[0]; - - # Remove association - $this->db->pq("DELETE FROM protein_has_pdb WHERE proteinhaspdbid=:1", array($this->arg('pdbid'))); - - # Remove entry if its the last one - $count = $this->db->pq("SELECT pdbid FROM protein_has_pdb WHERE pdbid=:1", array($pdb['PDBID'])); - if (!sizeof($count)) $this->db->pq("DELETE FROM pdb WHERE pdbid=:1", array($pdb['PDBID'])); - - $this->_output(1); - } - - - - function _add_protein() { - global $valid_components; - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('ACRONYM')) $this->_error('No protein acronym'); - - $name = $this->has_arg('NAME') ? $this->arg('NAME') : ''; - $seq = $this->has_arg('SEQUENCE') ? $this->arg('SEQUENCE') : ''; - $mass = $this->has_arg('MOLECULARMASS') ? $this->arg('MOLECULARMASS') : null; - $ct = $this->has_arg('CONCENTRATIONTYPEID') ? $this->arg('CONCENTRATIONTYPEID') : null; - $cmt = $this->has_arg('COMPONENTTYPEID') ? $this->arg('COMPONENTTYPEID') : null; - $global = $this->has_arg('GLOBAL') ? $this->arg('GLOBAL') : null; - $density = $this->has_arg('DENSITY') ? $this->arg('DENSITY') : null; - $externalid = $this->has_arg('EXTERNALID') ? $this->arg('EXTERNALID') : null; - $safetyLevel = $this->has_arg('SAFETYLEVEL') ? $this->arg('SAFETYLEVEL') : null; - - // Only staff should be able to create Proteins that are not approved (i.e. no EXTERNALID) in User System - if ($valid_components) { - if (!$externalid && !$this->staff) $this->_error('Only staff can create proteins that are not approved in ISPyB', 401); - - if (!$this->staff) { - $chkext = $this->db->pq("SELECT proteinid FROM protein + if (!sizeof($pdb)) + $this->_error('No such pdb'); + else + $pdb = $pdb[0]; + + # Remove association + $this->db->pq("DELETE FROM protein_has_pdb WHERE proteinhaspdbid=:1", array($this->arg('pdbid'))); + + # Remove entry if its the last one + $count = $this->db->pq("SELECT pdbid FROM protein_has_pdb WHERE pdbid=:1", array($pdb['PDBID'])); + if (!sizeof($count)) + $this->db->pq("DELETE FROM pdb WHERE pdbid=:1", array($pdb['PDBID'])); + + $this->_output(1); + } + + + + function _add_protein() + { + global $valid_components; + + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('ACRONYM')) + $this->_error('No protein acronym'); + + $name = $this->has_arg('NAME') ? $this->arg('NAME') : ''; + $seq = $this->has_arg('SEQUENCE') ? $this->arg('SEQUENCE') : ''; + $mass = $this->has_arg('MOLECULARMASS') ? $this->arg('MOLECULARMASS') : null; + $ct = $this->has_arg('CONCENTRATIONTYPEID') ? $this->arg('CONCENTRATIONTYPEID') : null; + $cmt = $this->has_arg('COMPONENTTYPEID') ? $this->arg('COMPONENTTYPEID') : null; + $global = $this->has_arg('GLOBAL') ? $this->arg('GLOBAL') : null; + $density = $this->has_arg('DENSITY') ? $this->arg('DENSITY') : null; + $externalid = $this->has_arg('EXTERNALID') ? $this->arg('EXTERNALID') : null; + $safetyLevel = $this->has_arg('SAFETYLEVEL') ? $this->arg('SAFETYLEVEL') : null; + + // Only staff should be able to create Proteins that are not approved (i.e. no EXTERNALID) in User System + if ($valid_components) { + if (!$externalid && !$this->staff) + $this->_error('Only staff can create proteins that are not approved in ISPyB', 401); + + if (!$this->staff) { + $chkext = $this->db->pq("SELECT proteinid FROM protein WHERE proposalid=:1 AND externalid=:2", array($this->proposalid, $externalid)); - if (sizeof($chkext)) $this->_error('No such protein to clone from'); - } + if (sizeof($chkext)) + $this->_error('No such protein to clone from'); } - - $chk = $this->db->pq("SELECT proteinid FROM protein + } + + $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proposalid=:1 AND acronym=:2", array($this->proposalid, $this->arg('ACRONYM'))); - if (sizeof($chk)) $this->_error('That protein acronym already exists in this proposal'); + if (sizeof($chk)) + $this->_error('That protein acronym already exists in this proposal'); - $this->db->pq('INSERT INTO protein (proteinid,proposalid,name,acronym,sequence,molecularmass,bltimestamp,concentrationtypeid,componenttypeid,global,density,externalid,safetylevel) + $this->db->pq( + 'INSERT INTO protein (proteinid,proposalid,name,acronym,sequence,molecularmass,bltimestamp,concentrationtypeid,componenttypeid,global,density,externalid,safetylevel) VALUES (s_protein.nextval,:1,:2,:3,:4,:5,CURRENT_TIMESTAMP,:6,:7,:8,:9,UNHEX(:10),:11) RETURNING proteinid INTO :id', - array($this->proposalid, $name, $this->arg('ACRONYM'), $seq, $mass, $ct, $cmt, $global, $density, $externalid, $safetyLevel)); - - $pid = $this->db->id(); + array($this->proposalid, $name, $this->arg('ACRONYM'), $seq, $mass, $ct, $cmt, $global, $density, $externalid, $safetyLevel) + ); - if ($seq) { - $this->_on_add_protein_sequence($pid); - } + $pid = $this->db->id(); - $this->_output(array('PROTEINID' => $pid)); + if ($seq) { + $this->_on_add_protein_sequence($pid); } + $this->_output(array('PROTEINID' => $pid)); + } - function _on_add_protein_sequence($pid) { - global $zocalo_recipes_on_add_protein_sequence; - if (!empty($zocalo_recipes_on_add_protein_sequence)) { - $parameters = array('ispyb_protein_id' => $pid); - foreach($zocalo_recipes_on_add_protein_sequence as $recipe){ - $this->_submit_zocalo_recipe($recipe, $parameters, 500); - } + + function _on_add_protein_sequence($pid) + { + global $zocalo_recipes_on_add_protein_sequence; + if (!empty($zocalo_recipes_on_add_protein_sequence)) { + $parameters = array('ispyb_protein_id' => $pid); + foreach ($zocalo_recipes_on_add_protein_sequence as $recipe) { + $this->_submit_zocalo_recipe($recipe, $parameters, 500); } } + } - # TODO: Consolidate this into clas.exp.php - # - Lazy workaround... - function _get_diffraction_plans() { - global $preset_proposal; - $where = 'dp.presetforproposalid=:1 OR (dp.presetforproposalid=:2)'; + # TODO: Consolidate this into clas.exp.php + # - Lazy workaround... + function _get_diffraction_plans() + { + global $preset_proposal; + $where = 'dp.presetforproposalid=:1 OR (dp.presetforproposalid=:2)'; - $preset = $this->db->pq("SELECT p.proposalid + $preset = $this->db->pq("SELECT p.proposalid FROM proposal p WHERE CONCAT(p.proposalcode,p.proposalnumber) LIKE :1", array($preset_proposal)); - if (sizeof($preset)) $presetid = $preset[0]['PROPOSALID']; - else $presetid = 0; + if (sizeof($preset)) + $presetid = $preset[0]['PROPOSALID']; + else + $presetid = 0; - $args = array($this->proposalid, $presetid); + $args = array($this->proposalid, $presetid); - if ($this->has_arg('did')) { - $where .= ' AND dp.diffractionplanid = :'.(sizeof($args)+1); - array_push($args, $this->arg('did')); - } + if ($this->has_arg('did')) { + $where .= ' AND dp.diffractionplanid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('did')); + } - if ($this->has_arg('BEAMLINENAME')) { - $where .= ' AND dp.beamlinename=:'.(sizeof($args)+1); - array_push($args, $this->arg('BEAMLINENAME')); - } + if ($this->has_arg('BEAMLINENAME')) { + $where .= ' AND dp.beamlinename=:' . (sizeof($args) + 1); + array_push($args, $this->arg('BEAMLINENAME')); + } - $dps = $this->db->pq("SELECT dp.diffractionplanid, dp.comments, dp.experimentkind, dp.preferredbeamsizex, dp.preferredbeamsizey, ROUND(dp.exposuretime, 6) as exposuretime, ROUND(dp.requiredresolution, 2) as requiredresolution, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy as energy, monochromator, beamlinename + $dps = $this->db->pq("SELECT dp.diffractionplanid, dp.comments, dp.experimentkind, dp.preferredbeamsizex, dp.preferredbeamsizey, ROUND(dp.exposuretime, 6) as exposuretime, ROUND(dp.requiredresolution, 2) as requiredresolution, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy as energy, monochromator, beamlinename FROM diffractionplan dp WHERE $where ", $args); - if ($this->has_arg('did')) { - if (sizeof($dps)) $this->_output($dps[0]); - else $this->_error('No such diffraction plan'); - - } else $this->_output($dps); - } + if ($this->has_arg('did')) { + if (sizeof($dps)) + $this->_output($dps[0]); + else + $this->_error('No such diffraction plan'); + } else + $this->_output($dps); + } - function _add_diffraction_plan() { - if (!$this->has_arg('COMMENTS')) $this->_error('No name specified'); + function _add_diffraction_plan() + { + if (!$this->has_arg('COMMENTS')) + $this->_error('No name specified'); - $args = array($this->arg('COMMENTS'), $this->proposalid); - foreach(array('EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'BEAMLINENAME') as $f) { - array_push($args, $this->has_arg($f) ? $this->arg($f) : null); - } + $args = array($this->arg('COMMENTS'), $this->proposalid); + foreach (array('EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'BEAMLINENAME') as $f) { + array_push($args, $this->has_arg($f) ? $this->arg($f) : null); + } - $this->db->pq("INSERT INTO diffractionplan (diffractionplanid, comments, presetforproposalid, experimentkind, requiredresolution, preferredbeamsizex, preferredbeamsizey, exposuretime, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy, monochromator, beamlinename) + $this->db->pq("INSERT INTO diffractionplan (diffractionplanid, comments, presetforproposalid, experimentkind, requiredresolution, preferredbeamsizex, preferredbeamsizey, exposuretime, boxsizex, boxsizey, axisstart, axisrange, numberofimages, transmission, energy, monochromator, beamlinename) VALUES (s_diffractionplan.nextval, :1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15, :16) RETURNING diffractionplanid INTO :id", $args); - $this->_output(array('DIFFRACTIONPLANID' => $this->db->id())); - } + $this->_output(array('DIFFRACTIONPLANID' => $this->db->id())); + } - function _update_diffraction_plan() { - $dp = $this->db->pq("SELECT dp.diffractionplanid + function _update_diffraction_plan() + { + $dp = $this->db->pq("SELECT dp.diffractionplanid FROM diffractionplan dp WHERE dp.diffractionplanid=:1 AND dp.presetforproposalid=:2", array($this->arg('pid'), $this->proposalid)); - if (!sizeof($dp)) $this->_error('No such diffraction plan'); - $dp = $dp[0]; + if (!sizeof($dp)) + $this->_error('No such diffraction plan'); + $dp = $dp[0]; - $sfields = array('EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'COMMENTS', 'BEAMLINENAME'); - foreach ($sfields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE diffractionplan SET $f=:1 WHERE diffractionplanid=:2", array($this->arg($f), $dp['DIFFRACTIONPLANID'])); - $this->_output(array($f => $this->arg($f))); - } + $sfields = array('EXPERIMENTKIND', 'REQUIREDRESOLUTION', 'PREFERREDBEAMSIZEX', 'PREFERREDBEAMSIZEY', 'EXPOSURETIME', 'BOXSIZEX', 'BOXSIZEY', 'AXISSTART', 'AXISRANGE', 'NUMBEROFIMAGES', 'TRANSMISSION', 'ENERGY', 'MONOCHROMATOR', 'COMMENTS', 'BEAMLINENAME'); + foreach ($sfields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE diffractionplan SET $f=:1 WHERE diffractionplanid=:2", array($this->arg($f), $dp['DIFFRACTIONPLANID'])); + $this->_output(array($f => $this->arg($f))); } } + } + + + function _delete_diffraction_plan() + { + } + + + function _crystals() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - function _delete_diffraction_plan() { + $args = array($this->proposalid); + $where = 'pr.proposalid=:1'; + $join = ''; + # For a specific protein + if ($this->has_arg('pid')) { + $where .= ' AND (pr.proteinid=:' . (sizeof($args) + 1) . ' OR chc2.componentid=:' . (sizeof($args) + 2) . ')'; + $join .= ' LEFT OUTER JOIN blsampletype_has_component chc2 ON chc2.blsampletypeid=cr.crystalid'; + array_push($args, $this->arg('pid')); + array_push($args, $this->arg('pid')); } + # For a particular crystal + if ($this->has_arg('CRYSTALID')) { + $where .= ' AND cr.crystalid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CRYSTALID')); + } + $cseq = ''; + if ($this->has_arg('seq')) { + $cseq = 'string_agg(cpr.sequence) as componentsequences,'; + } - function _crystals() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $args = array($this->proposalid); - $where = 'pr.proposalid=:1'; - $join = ''; - - # For a specific protein - if ($this->has_arg('pid')) { - $where .= ' AND (pr.proteinid=:'.(sizeof($args)+1).' OR chc2.componentid=:'.(sizeof($args)+2).')'; - $join .= ' LEFT OUTER JOIN blsampletype_has_component chc2 ON chc2.blsampletypeid=cr.crystalid'; - array_push($args, $this->arg('pid')); - array_push($args, $this->arg('pid')); - } - - # For a particular crystal - if ($this->has_arg('CRYSTALID')) { - $where .= ' AND cr.crystalid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CRYSTALID')); - } - $cseq = ''; - if ($this->has_arg('seq')) { - $cseq = 'string_agg(cpr.sequence) as componentsequences,'; - } - - - // Search - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(cr.name) LIKE lower(CONCAT(CONCAT('%',:".$st."),'%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) OR lower(cr.comments) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')))"; - for ($i = 0; $i < 3; $i++) array_push($args, $this->arg('s')); - } - - $tot = $this->db->pq("SELECT count(distinct cr.crystalid) as tot + // Search + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(cr.name) LIKE lower(CONCAT(CONCAT('%',:" . $st . "),'%')) OR lower(pr.acronym) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) OR lower(cr.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')))"; + for ($i = 0; $i < 3; $i++) + array_push($args, $this->arg('s')); + } + + $tot = $this->db->pq("SELECT count(distinct cr.crystalid) as tot FROM crystal cr INNER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN blsampletype_has_component chc ON cr.crystalid = chc.blsampletypeid INNER JOIN proposal p ON p.proposalid = pr.proposalid $join WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'cr.crystalid DESC'; - - - if ($this->has_arg('sort_by')) { - $cols = array('CRYSTALID' => 'cr.crystalid', 'NAME' => 'cr.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'cr.comments'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $rows = $this->db->paginate("SELECT distinct cr.crystalid, cr.name, CONCAT(p.proposalcode,p.proposalnumber) as prop, pr.acronym, pr.name as protein, pr.sequence, pr.proteinid, cr.spacegroup,cr.comments,cr.name,cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma, cr.comments, cr.theoreticaldensity, pr.density + + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'cr.crystalid DESC'; + + + if ($this->has_arg('sort_by')) { + $cols = array('CRYSTALID' => 'cr.crystalid', 'NAME' => 'cr.name', 'ACRONYM' => 'pr.acronym', 'SPACEGROUP' => 'cr.spacegroup', 'COMMENTS' => 'cr.comments'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + + $rows = $this->db->paginate("SELECT distinct cr.crystalid, cr.name, CONCAT(p.proposalcode,p.proposalnumber) as prop, pr.acronym, pr.name as protein, pr.sequence, pr.proteinid, cr.spacegroup,cr.comments,cr.name,cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma, cr.comments, cr.theoreticaldensity, pr.density ,string_agg(cpr.proteinid) as componentids, string_agg(cpr.name) as componentnames, string_agg(cpr.acronym) as componentacronyms, string_agg(cpr.density) as componentdensities, $cseq string_agg(cpr.global) as componentglobals, string_agg(chc.abundance) as componentamounts, string_agg(ct.symbol) as componenttypesymbols, pct.symbol,ROUND(cr.abundance,3) as abundance FROM crystal cr @@ -2118,500 +2381,715 @@ function _crystals() { GROUP BY cr.crystalid, pr.acronym, pr.proteinid, cr.spacegroup, CONCAT(p.proposalcode,p.proposalnumber), cr.cell_a, cr.cell_b, cr.cell_c, cr.cell_alpha, cr.cell_beta, cr.cell_gamma ORDER BY $order", $args); - - foreach ($rows as &$r) { - foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTDENSITIES', 'COMPONENTNAMES', 'COMPONENTSEQUENCES', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS') as $k) { - if (array_key_exists($k, $r)) { - if ($r[$k]) $r[$k] = explode(',', $r[$k]); - } + + foreach ($rows as &$r) { + foreach (array('COMPONENTIDS', 'COMPONENTAMOUNTS', 'COMPONENTDENSITIES', 'COMPONENTNAMES', 'COMPONENTSEQUENCES', 'COMPONENTACRONYMS', 'COMPONENTTYPESYMBOLS', 'COMPONENTGLOBALS') as $k) { + if (array_key_exists($k, $r)) { + if ($r[$k]) + $r[$k] = explode(',', $r[$k]); } } - - - if ($this->has_arg('CRYSTALID')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such crystal'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); } - function _add_crystal() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('PROTEINID')) $this->_error('No protein specified'); - if (!$this->has_arg('NAME')) $this->_error('No name specified'); + if ($this->has_arg('CRYSTALID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such crystal'); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } + + + function _add_crystal() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('PROTEINID')) + $this->_error('No protein specified'); + if (!$this->has_arg('NAME')) + $this->_error('No name specified'); + + $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proteinid=:1 AND proposalid=:2", array($this->arg('PROTEINID'), $this->proposalid)); + if (!sizeof($chk)) + $this->_error('No such protein'); + + $a = array(); + foreach (array('SPACEGROUP', 'COMMENTS', 'NAME') as $f) + $a[$f] = $this->has_arg($f) ? $this->arg($f) : ''; + foreach (array('CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'THEORETICALDENSITY') as $f) + $a[$f] = $this->has_arg($f) ? $this->arg($f) : null; + + $this->db->pq( + "INSERT INTO crystal (crystalid,proteinid,spacegroup,cell_a,cell_b,cell_c,cell_alpha,cell_beta,cell_gamma,abundance,comments,name,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12) RETURNING crystalid INTO :id", + array($this->arg('PROTEINID'), $a['SPACEGROUP'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['ABUNDANCE'], $a['COMMENTS'], $a['NAME'], $a['THEORETICALDENSITY']) + ); + $crysid = $this->db->id(); - $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proteinid=:1 AND proposalid=:2", array($this->arg('PROTEINID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such protein'); + if ($this->has_arg('COMPONENTIDS')) + $this->_update_sample_components(array(), $this->arg('COMPONENTIDS'), $this->arg('COMPONENTAMOUNTS'), $crysid); - $a = array(); - foreach (array('SPACEGROUP', 'COMMENTS', 'NAME') as $f) $a[$f] = $this->has_arg($f) ? $this->arg($f) : ''; - foreach (array('CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'THEORETICALDENSITY') as $f) $a[$f] = $this->has_arg($f) ? $this->arg($f) : null; + $this->_output(array('CRYSTALID' => $crysid)); + } - $this->db->pq("INSERT INTO crystal (crystalid,proteinid,spacegroup,cell_a,cell_b,cell_c,cell_alpha,cell_beta,cell_gamma,abundance,comments,name,theoreticaldensity) VALUES (s_crystal.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12) RETURNING crystalid INTO :id", - array($this->arg('PROTEINID'), $a['SPACEGROUP'], $a['CELL_A'], $a['CELL_B'], $a['CELL_C'], $a['CELL_ALPHA'], $a['CELL_BETA'], $a['CELL_GAMMA'], $a['ABUNDANCE'], $a['COMMENTS'], $a['NAME'], $a['THEORETICALDENSITY'])); - $crysid = $this->db->id(); - - if ($this->has_arg('COMPONENTIDS')) $this->_update_sample_components(array(), $this->arg('COMPONENTIDS'), $this->arg('COMPONENTAMOUNTS'), $crysid); - - $this->_output(array('CRYSTALID' => $crysid)); - } + function _update_crystal() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('CRYSTALID')) + $this->_error('No crystal specified'); - function _update_crystal() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('CRYSTALID')) $this->_error('No crystal specified'); - - $chk = $this->db->pq("SELECT pr.proteinid, string_agg(chc.componentid) as componentids + $chk = $this->db->pq("SELECT pr.proteinid, string_agg(chc.componentid) as componentids FROM crystal cr INNER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN blsampletype_has_component chc ON cr.crystalid = chc.blsampletypeid WHERE cr.crystalid=:1 AND pr.proposalid=:2", array($this->arg('CRYSTALID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such crystal'); - $chk = $chk[0]; - - $cfields = array('PROTEINID', 'SPACEGROUP', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'COMMENTS', 'NAME', 'THEORETICALDENSITY'); - foreach ($cfields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE crystal SET $f=:1 WHERE crystalid=:2", array($this->arg($f), $this->arg('CRYSTALID'))); - - if ($f == 'PROTEINID') { - $name = $this->db->pq('SELECT acronym FROM protein WHERE proteinid=:1', array($chk['PROTEINID'])); - if (sizeof($name)) { - $this->_output(array('ACRONYM' => $name[0]['ACRONYM'])); - } - } else $this->_output(array($f => $this->arg($f))); - } + if (!sizeof($chk)) + $this->_error('No such crystal'); + $chk = $chk[0]; + + $cfields = array('PROTEINID', 'SPACEGROUP', 'CELL_A', 'CELL_B', 'CELL_C', 'CELL_ALPHA', 'CELL_BETA', 'CELL_GAMMA', 'ABUNDANCE', 'COMMENTS', 'NAME', 'THEORETICALDENSITY'); + foreach ($cfields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE crystal SET $f=:1 WHERE crystalid=:2", array($this->arg($f), $this->arg('CRYSTALID'))); + + if ($f == 'PROTEINID') { + $name = $this->db->pq('SELECT acronym FROM protein WHERE proteinid=:1', array($chk['PROTEINID'])); + if (sizeof($name)) { + $this->_output(array('ACRONYM' => $name[0]['ACRONYM'])); + } + } else + $this->_output(array($f => $this->arg($f))); } + } - if ($this->has_arg('COMPONENTIDS') && $this->has_arg('COMPONENTAMOUNTS')) { - $init_comps = explode(',', $chk['COMPONENTIDS']); - $this->_update_sample_components($init_comps, $this->arg('COMPONENTIDS'), $this->arg('COMPONENTAMOUNTS'), $this->arg('CRYSTALID')); - } + if ($this->has_arg('COMPONENTIDS') && $this->has_arg('COMPONENTAMOUNTS')) { + $init_comps = explode(',', $chk['COMPONENTIDS']); + $this->_update_sample_components($init_comps, $this->arg('COMPONENTIDS'), $this->arg('COMPONENTAMOUNTS'), $this->arg('CRYSTALID')); } + } + function _protein_lattices() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + $where = 'pr.proposalid=:1'; + $args = array($this->proposalid); - function _protein_lattices() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $where = 'pr.proposalid=:1'; - $args = array($this->proposalid); + if ($this->has_arg('lid')) { + $where .= ' AND l.proteinhaslatticeid=:2'; + array_push($args, $this->arg('lid')); + } - if ($this->has_arg('lid')) { - $where .= ' AND l.proteinhaslatticeid=:2'; - array_push($args, $this->arg('lid')); - } - - if ($this->has_arg('pid')) { - $where .= ' AND pr.proteinid=:2'; - array_push($args, $this->arg('pid')); - } + if ($this->has_arg('pid')) { + $where .= ' AND pr.proteinid=:2'; + array_push($args, $this->arg('pid')); + } - $tot = $this->db->pq("SELECT count(distinct l.componentlatticeid) as tot + $tot = $this->db->pq("SELECT count(distinct l.componentlatticeid) as tot FROM componentlattice l INNER JOIN protein pr ON pr.proteinid = l.componentid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); + $tot = intval($tot[0]['TOT']); - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; - $rows = $this->db->paginate("SELECT l.componentlatticeid, pr.proteinid, l.spacegroup, l.cell_a, l.cell_b, l.cell_c, l.cell_alpha, l.cell_beta, l.cell_gamma - FROM componentlattice l - INNER JOIN protein pr ON pr.proteinid = l.componentid - WHERE $where", $args); - - if ($this->has_arg('pid')) { - if (!sizeof($rows)) $this->_error('No such lattice'); - else $this->_output($rows[0]); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; } - function _add_protein_lattice() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('PROTEINID')) $this->_error('No protein specified'); - - $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proteinid=:1 AND proposalid=:2", array($this->arg('PROTEINID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such protein'); - + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); - } - - function _update_protein_lattice() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('CRYSTALID')) $this->_error('No crystal specified'); + $rows = $this->db->paginate("SELECT l.componentlatticeid, pr.proteinid, l.spacegroup, l.cell_a, l.cell_b, l.cell_c, l.cell_alpha, l.cell_beta, l.cell_gamma + FROM componentlattice l + INNER JOIN protein pr ON pr.proteinid = l.componentid + WHERE $where", $args); - $chk = $this->db->pq("SELECT pr.proteinid FROM componentlattice l INNER JOIN protein pr ON l.proteinid = cr.proteinid + if ($this->has_arg('pid')) { + if (!sizeof($rows)) + $this->_error('No such lattice'); + else + $this->_output($rows[0]); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } + + function _add_protein_lattice() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('PROTEINID')) + $this->_error('No protein specified'); + + $chk = $this->db->pq("SELECT proteinid FROM protein WHERE proteinid=:1 AND proposalid=:2", array($this->arg('PROTEINID'), $this->proposalid)); + if (!sizeof($chk)) + $this->_error('No such protein'); + } + + function _update_protein_lattice() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('CRYSTALID')) + $this->_error('No crystal specified'); + + $chk = $this->db->pq("SELECT pr.proteinid FROM componentlattice l INNER JOIN protein pr ON l.proteinid = cr.proteinid WHERE l.componentlatticeid=:1 AND pr.proposalid=:2", array($this->arg('COMPONENTLATTICEID'), $this->proposalid)); - if (!sizeof($chk)) $this->_error('No such lattice'); - } - - - - - function _concentration_types() { - $rows = $this->db->pq("SELECT concentrationtypeid, symbol, name FROM concentrationtype"); - $this->_output($rows); - } - - - function _component_types() { - $rows = $this->db->pq("SELECT componenttypeid, name FROM componenttype"); - $this->_output($rows); - } - - - function _component_sub_types() { - $rows = $this->db->pq("SELECT componentsubtypeid, name FROM componentsubtype"); - $this->_output($rows); - } - - - - # Sample Groups - function _build_sample_groups_query($where, $fields, $from_table, $joins, $group = '') - { - return "SELECT $fields $from_table $joins WHERE $where $group"; - } - - function _sample_groups() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + if (!sizeof($chk)) + $this->_error('No such lattice'); + } + + + + + function _concentration_types() + { + $rows = $this->db->pq("SELECT concentrationtypeid, symbol, name FROM concentrationtype"); + $this->_output($rows); + } + + + function _component_types() + { + $rows = $this->db->pq("SELECT componenttypeid, name FROM componenttype"); + $this->_output($rows); + } + + + function _component_sub_types() + { + $rows = $this->db->pq("SELECT componentsubtypeid, name FROM componentsubtype"); + $this->_output($rows); + } + + # Sample Groups + function _build_sample_groups_query($where, $fields, $from_table, $joins, $group = '') + { + return "SELECT $fields $from_table $joins WHERE $where $group"; + } + + function _sample_groups() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + $where = 'bsg.proposalid = :1'; + $args = array($this->proposalid); + $total_select_field = 'count(*) as total'; + $joins = ''; + $from_table = ''; + $group_by = ''; + + // Check if we are grouping the result by BlSAMPLEID or BLSAMPLEGROUPID. + // This is currently being used by xpdf when fetching the list of sample group samples. + if ($this->has_arg('groupSamplesType') && $this->arg('groupSamplesType') === 'BLSAMPLEGROUPID') { + $select_fields = 'bsg.blsamplegroupid, bsg.name, count(bshg.blsampleid) as samplegroupsamples, GROUP_CONCAT(distinct(c.containerid)) AS containerids, GROUP_CONCAT(DISTINCT( IF(c.code IS NOT NULL, c.code, c.barcode))) AS containers'; + $from_table = 'FROM blsamplegroup bsg'; + $joins = ' + LEFT JOIN blsamplegroup_has_blsample bshg ON bshg.blsamplegroupid = bsg.blsamplegroupid + LEFT JOIN blsample bs on bshg.blsampleid = bs.blsampleid + LEFT JOIN container c on bs.containerid = c.containerid + '; + $group_by .= 'GROUP BY bsg.blsamplegroupid'; + + $total_sub_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); + $total_query = "SELECT count(*) as total FROM ($total_sub_query) as total"; + } else if ($this->has_arg('BLSAMPLEID')) { + $select_fields = 'bsg.blsamplegroupid, bsg.name'; + $from_table = 'FROM blsamplegroup bsg'; + $joins = 'LEFT JOIN blsamplegroup_has_blsample bshg ON bshg.blsamplegroupid = bsg.blsamplegroupid'; + $where .= ' AND bshg.blsampleid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEID')); - $where = 'bsg.proposalid = :1'; - $args = array($this->proposalid); - $total_select_field = 'count(*) as total'; - $joins = ''; - $from_table = ''; - $group_by = ''; - - // Check if we are grouping the result by BlSAMPLEID or BLSAMPLEGROUPID. - // This is currently being used by xpdf when fetching the list of sample group samples. - if ($this->has_arg('groupSamplesType') && $this->arg('groupSamplesType') === 'BLSAMPLEGROUPID') { - $select_fields = 'bsg.blsamplegroupid, bsg.name, count(bshg.blsampleid) as samplegroupsamples'; - $from_table = 'FROM blsamplegroup bsg'; - $joins = 'LEFT JOIN blsamplegroup_has_blsample bshg ON bshg.blsamplegroupid = bsg.blsamplegroupid'; - $group_by .= 'GROUP BY bsg.blsamplegroupid'; - - $total_sub_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); - $total_query = "SELECT count(*) as total FROM ($total_sub_query) as total"; - } else { - $select_fields = 'bsg.blSampleGroupId, bsg.name, bshg.blSampleId, bshg.type, b.name as sample, cr.crystalId, cr.name as crystal'; - $from_table = 'FROM blsample b'; - $joins = ' + $total_sub_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); + $total_query = "SELECT count(*) as total FROM ($total_sub_query) as total"; + } else { + $select_fields = 'bsg.blSampleGroupId, bsg.name, bshg.blSampleId, bshg.type, b.name as sample, cr.crystalId, cr.name as crystal'; + $from_table = 'FROM blsample b'; + $joins = ' INNER JOIN blsamplegroup_has_blsample bshg ON bshg.blsampleid = b.blsampleid INNER JOIN blsamplegroup bsg ON bshg.blsamplegroupid = bsg.blsamplegroupid INNER JOIN crystal cr ON cr.crystalid = b.crystalid '; - $total_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); - } + $total_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); + } - $tot = $this->db->pq($total_query, $args); + $tot = $this->db->pq($total_query, $args); - $tot = intval($tot[0]['TOTAL']); + $tot = intval($tot[0]['TOTAL']); - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); - $rows_query = $this->_build_sample_groups_query($where, $select_fields, $from_table, $joins, $group_by); - $rows = $this->db->paginate($rows_query, $args); + $rows_query = $this->_build_sample_groups_query($where, $select_fields, $from_table, $joins, $group_by); + $rows = $this->db->paginate($rows_query, $args); - $this->_output(array( - 'total' => $tot, - 'data' => $rows, - )); - } + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } - function _add_new_sample_group() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + function _add_new_sample_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - $sgid = $this->_create_sample_group(); + $sgid = $this->_create_sample_group(); - $this->_output($sgid); + $this->_output($sgid); - $this->_output(array( - 'BLSAMPLEGROUPID' => $sgid, - 'NAME' => $this->has_arg('NAME') ? $this->arg('NAME') : NULL, - 'SAMPLEGROUPSAMPLES' => 0 - )); - } + $this->_output(array( + 'BLSAMPLEGROUPID' => $sgid, + 'NAME' => $this->has_arg('NAME') ? $this->arg('NAME') : NULL, + 'SAMPLEGROUPSAMPLES' => 0 + )); + } - function _create_sample_group() { - $name = $this->has_arg('NAME') ? $this->arg('NAME') : NULL; - $this->db->pq("INSERT INTO blsamplegroup (blsamplegroupid, name, proposalid) VALUES(NULL, :1, :2)", array($name, $this->proposalid)); - return $this->db->id(); - } + function _create_sample_group() + { + $name = $this->has_arg('NAME') ? $this->arg('NAME') : NULL; + $this->db->pq("INSERT INTO blsamplegroup (blsamplegroupid, name, proposalid) VALUES(NULL, :1, :2)", array($name, $this->proposalid)); + return $this->db->id(); + } - function _get_sample_groups_by_sample() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + function _get_sample_groups_by_sample() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - $where = 'bsg.proposalid = :1'; - $args = array($this->proposalid); + $where = 'bsg.proposalid = :1'; + $args = array($this->proposalid); - if (!$this->has_arg('BLSAMPLEID')) $this->_error('No sample specified'); + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); - $where .= ' AND b.blsampleid = :2'; - array_push($args, $this->arg('BLSAMPLEID')); + $where .= ' AND b.blsampleid = :2'; + array_push($args, $this->arg('BLSAMPLEID')); - $tot = $this->db->pq("SELECT count(*) as total + $tot = $this->db->pq("SELECT count(*) as total FROM blsamplegroup bsg INNER JOIN blsamplegroup_has_blsample bshg on bsg.blsamplegroupId = bshg.blsamplegroupid INNER JOIN blsample b on bshg.blsampleId = b.blsampleid WHERE $where", $args); - $tot = intval($tot[0]['TOTAL']); + $tot = intval($tot[0]['TOTAL']); - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); - $rows = $this->db->paginate("SELECT bsg.blsamplegroupid, bsg.name + $rows = $this->db->paginate("SELECT bsg.blsamplegroupid, bsg.name FROM blsamplegroup bsg INNER JOIN blsamplegroup_has_blsample bshg on bsg.blsamplegroupid = bshg.blsamplegroupid INNER JOIN blsample b on bshg.blsampleid = b.blsampleid WHERE $where", $args); - $this->_output(array( - 'total' => $tot, - 'data' => $rows, - )); + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } + + function _get_sample_group_samples() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); + + $where = 'bsg.proposalid = :1'; + $args = array($this->proposalid); + $total_select_field = 'count(*) as total'; + $joins = ''; + $from_table = ''; + $group_by = ''; + + $select_fields = " + b.blsampleid, + bshg.blsamplegroupid, + bshg.grouporder, + bshg.type, + b.name as sample, + b.dimension1, + b.dimension2, + b.dimension3, + b.shape, + b.location, + b.packingfraction, + cr.theoreticaldensity, + b.blsampleid, + cr.crystalid, + cr.name as crystal, + CONCAT(bshg.blsamplegroupid, '-', b.blsampleid) as blsamplegroupsampleid, + c.containerid, + IF(c.code IS NOT NULL, c.code, c.barcode) as container, + c.capacity + "; + $from_table = 'FROM blsample b'; + $joins = ' + INNER JOIN blsamplegroup_has_blsample bshg on b.blsampleid = bshg.blsampleid + INNER JOIN blsamplegroup bsg ON bshg.blsamplegroupid = bsg.blsamplegroupid + INNER JOIN crystal cr on cr.crystalid = b.crystalid + INNER JOIN container c on c.containerid = b.containerid + '; + $where .= ' AND bsg.blsamplegroupid =:' . (sizeof($args) + 1); + array_push($args, $this->arg('BLSAMPLEGROUPID')); + + $total_query = $this->_build_sample_groups_query($where, $total_select_field, $from_table, $joins, $group_by); + + $tot = $this->db->pq($total_query, $args); + + $tot = intval($tot[0]['TOTAL']); + + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; + + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; } - function _get_sample_group_samples() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); - if (!$this->has_arg('BLSAMPLEGROUPID')) $this->error('No sample group specified'); + $rows_query = $this->_build_sample_groups_query($where, $select_fields, $from_table, $joins, $group_by); + $rows = $this->db->paginate($rows_query, $args); - $where = 'bsg.proposalid = :1'; - $args = array($this->proposalid); + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } - $where .= ' AND bsg.blsamplegroupid = :2'; - array_push($args, $this->arg('BLSAMPLEGROUPID')); + function _save_sample_to_group($blSampleId, $blSampleGroupId, $groupOrder, $type) + { + if (!isset($blSampleId)) return 'No sample specified'; - $tot = $this->db->pq("SELECT count(*) as total - FROM blsample b - INNER JOIN blsamplegroup_has_blsample bshg ON bshg.blsampleid = b.blsampleid - INNER JOIN blsamplegroup bsg ON bsg.blsamplegroupid = bshg.blsamplegroupid - WHERE $where", $args); + if (!isset($blSampleGroupId)) + return 'No sample group specified. Create one before adding samples to group.'; - $tot = intval($tot[0]['TOTAL']); + $sgid = $blSampleGroupId; - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; + $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid + FROM blsample b + INNER JOIN crystal cr ON cr.crystalid = b.crystalid + INNER JOIN protein pr ON pr.proteinid = cr.proteinid + LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid + WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $blSampleId)); - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } + if (!sizeof($samp)) + return 'No such sample'; + else + $samp = $samp[0]; - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); + $sample_group_exists = $this->db->pq("SELECT bsg.blsamplegroupid + FROM blsamplegroup bsg + WHERE bsg.blsamplegroupid = :1 AND bsg.proposalid = :2", array($sgid, $this->proposalid)); + if (!sizeof($sample_group_exists)) $this->_error('No such sample group'); - $rows = $this->db->paginate("SELECT b.blsampleid, bshg.blsamplegroupid, bshg.grouporder, bshg.type, CONCAT(bshg.blsamplegroupid, '-', b.blsampleid) as blsamplegroupsampleid, b.name as sample, b.dimension1, b.dimension2, b.dimension3, b.shape, b.packingfraction, cr.theoreticaldensity, b.blsampleid, cr.crystalid, cr.name as crystal, c.code as container, pr.name as protein, bsg.name, pr.proteinid - FROM blsample b - INNER JOIN blsamplegroup_has_blsample bshg ON bshg.blsampleid = b.blsampleid - INNER JOIN blsamplegroup bsg ON bshg.blsamplegroupid = bsg.blsamplegroupid - INNER JOIN crystal cr ON cr.crystalid = b.crystalid - INNER JOIN protein pr ON pr.proteinid = cr.proteinid - INNER JOIN container c ON c.containerid = b.containerid - WHERE $where - ", $args); + $sample_exists_in_group = $this->db->pq(" + SELECT blsampleid + FROM blsamplegroup_has_blsample + WHERE blsampleid = :1 AND blsamplegroupid = :2 + ", array($blSampleId, $sgid)); - $this->_output(array( - 'total' => $tot, - 'data' => $rows, - )); + if (count($sample_exists_in_group) > 0) { + return array('BLSAMPLEGROUPSAMPLEID' => $sgid . '-' . $blSampleId, 'BLSAMPLEID' => $blSampleId, 'BLSAMPLEGROUPID' => $sgid); } - function _add_sample_to_group() { - if (!$this->has_arg('BLSAMPLEID')) $this->_error('No sample specified'); - if (!$this->has_arg('BLSAMPLEGROUPID')) $this->_error('No sample group specified'); + $order = isset($groupOrder) ? $groupOrder : 1; + $type = isset($type) ? $type : null; - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid + $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) + VALUES (:1,:2, :3, :4)", array($blSampleId, $sgid, $order, $type)); + + return array('BLSAMPLEGROUPSAMPLEID' => $sgid . '-' . $blSampleId, 'BLSAMPLEID' => $blSampleId, 'BLSAMPLEGROUPID' => $sgid); + } + + function _update_sample_in_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); + if (!$this->has_arg('BLSAMPLEGROUPSAMPLEID')) + $this->_error('No group sample id specified'); + + $gid = $this->arg('BLSAMPLEGROUPID'); + $arr = explode('-', $this->arg('BLSAMPLEGROUPSAMPLEID')); + $sid = end($arr); + + $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid FROM blsample b INNER JOIN crystal cr ON cr.crystalid = b.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid - WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid,$this->arg('BLSAMPLEID'))); + WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $sid)); - if (!sizeof($samp)) $this->_error('No such sample'); - else $samp = $samp[0]; + if (!sizeof($samp)) + $this->_error('No such sample'); + else + $samp = $samp[0]; - $sgid = $this->arg('BLSAMPLEGROUPID'); + $types = array('sample', 'container', 'background', 'calibrant'); + $fields = array('GROUPORDER', 'TYPE'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE blsamplegroup_has_blsample SET $f=:1 WHERE blsampleid=:2 AND blsamplegroupid=:3", array($this->arg($f), $sid, $gid)); + $this->_output(array($f => $this->arg($f))); + } + } + } - $sample_group_exists = $this->db->pq("SELECT bsg.blsamplegroupid - FROM blsamplegroup bsg - WHERE bsg.blsamplegroupid = :1 AND bsg.proposalid = :2", array($sgid, $this->proposalid)); - if (!sizeof($sample_group_exists)) $this->_error('No such sample group'); + function _get_sample_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); - $order = $this->has_arg('GROUPORDER') ? $this->arg('GROUPORDER') : 1; - $type = $this->has_arg('TYPE') ? $this->arg('TYPE') : null; + $sampleGroup = $this->db->pq("SELECT * FROM blsamplegroup WHERE blsamplegroupid = :1", array($this->arg('BLSAMPLEGROUPID'))); + + $this->_output($sampleGroup[0]); + } - $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) - VALUES (:1,:2, :3, :4)", array($this->arg('BLSAMPLEID'), $sgid, $order, $type)); + function _update_sample_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); - $this->_output(array('BLSAMPLEGROUPSAMPLEID' => $sgid.'-'.$this->arg('BLSAMPLEID'), 'BLSAMPLEID' => $this->arg('BLSAMPLEID'), 'BLSAMPLEGROUPID' => $sgid)); + $fields = array('NAME'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE blsamplegroup SET $f=:1 WHERE blsamplegroupid=:2", array($this->arg($f), $this->arg('BLSAMPLEGROUPID'))); + $this->_output(array($f => $this->arg($f), 'BLSAMPLEGROUPID' => $this->arg('BLSAMPLEGROUPID'))); + } } + } - function _save_sample_to_group($blSampleId, $blSampleGroupId, $groupOrder, $type) { - if (!isset($blSampleId)) return 'No sample specified'; + function _remove_sample_from_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); + if (!$this->has_arg('BLSAMPLEGROUPSAMPLEID')) + $this->_error('No group sample id specified'); - if (!isset($blSampleGroupId)) return 'No sample group specified. Create one before adding samples to group.'; + $gid = $this->arg('BLSAMPLEGROUPID'); + $arr = explode('-', $this->arg('BLSAMPLEGROUPSAMPLEID')); + $sid = end($arr); - $sgid = $blSampleGroupId; + $this->_delete_sample_from_group($gid, $sid); - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid - FROM blsample b - INNER JOIN crystal cr ON cr.crystalid = b.crystalid - INNER JOIN protein pr ON pr.proteinid = cr.proteinid - LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid - WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $blSampleId)); + $this->_output(new \stdClass); + } + + function _add_samples_to_group() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - if (!sizeof($samp)) return 'No such sample'; - else $samp = $samp[0]; + // Register entire container + if ($this->has_arg('collection')) { + $this->db->start_transaction(); + $collection = array(); + foreach ($this->arg('collection') as $sample) { + $blSampleGroupId = isset($sample['BLSAMPLEGROUPID']) ? $sample['BLSAMPLEGROUPID'] : null; + $blSampleId = isset($sample['BLSAMPLEID']) ? $sample['BLSAMPLEID'] : null; + $groupOrder = isset($sample['GROUPORDER']) ? $sample['GROUPORDER'] : null; + $type = isset($sample['TYPE']) ? $sample['TYPE'] : null; - $sample_exists = $this->db->pq("SELECT blsampleid FROM blsamplegroup_has_blsample WHERE blsampleid = :1 AND blsamplegroupid = :2", array($blSampleId, $sgid)); + $sample_group_result = $this->_save_sample_to_group($blSampleId, $blSampleGroupId, $groupOrder, $type); - if (count($sample_exists) > 0) { - return array('BLSAMPLEGROUPSAMPLEID' => $sgid.'-'.$blSampleId, 'BLSAMPLEID' => $blSampleId, 'BLSAMPLEGROUPID' => $sgid); + if (is_string($sample_group_result)) + $this->_error($sample_group_result); + + array_push($collection, $sample_group_result); } - $order = isset($groupOrder) ? $groupOrder: 1; - $type = isset($type) ? $type : null; + $this->db->end_transaction(); - $this->db->pq("INSERT INTO blsamplegroup_has_blsample (blsampleid, blsamplegroupid, grouporder, type) - VALUES (:1,:2, :3, :4)", array($blSampleId, $sgid, $order, $type)); + $this->_output($collection); + } else { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('BLSAMPLEID')) + $this->_error('No sample specified'); + if (!$this->has_arg('BLSAMPLEGROUPID')) + $this->_error('No sample group specified'); - return array('BLSAMPLEGROUPSAMPLEID' => $sgid.'-'.$blSampleId, 'BLSAMPLEID' => $blSampleId, 'BLSAMPLEGROUPID' => $sgid); - } + $sgid = $this->arg('BLSAMPLEGROUPID'); + $order = $this->has_arg('GROUPORDER') ? $this->arg('GROUPORDER') : 1; + $type = $this->has_arg('TYPE') ? $this->arg('TYPE') : null; - function _update_sample_in_group() { - if (!$this->has_arg('BLSAMPLEGROUPID')) $this->_error('No sample group specified'); - if (!$this->has_arg('BLSAMPLEGROUPSAMPLEID')) $this->_error('No group sample id specified'); + $result = $this->_save_sample_to_group($this->arg('BLSAMPLEID'), $sgid, $order, $type); - $gid = $this->arg('BLSAMPLEGROUPID'); - $arr = explode('-', $this->arg('BLSAMPLEGROUPSAMPLEID')); - $sid = end($arr); + $this->_output($result); + } + } - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid + function _delete_sample_from_group($blSampleGroupId, $blSampleGroupSampleId) + { + $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid FROM blsample b INNER JOIN crystal cr ON cr.crystalid = b.crystalid INNER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid - WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $sid)); - - if (!sizeof($samp)) $this->_error('No such sample'); - else $samp = $samp[0]; + WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $blSampleGroupSampleId)); - $types = array('sample', 'container', 'background', 'calibrant'); - $fields = array('GROUPORDER', 'TYPE'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE blsamplegroup_has_blsample SET $f=:1 WHERE blsampleid=:2 AND blsamplegroupid=:3", array($this->arg($f), $sid, $gid)); - $this->_output(array($f => $this->arg($f))); - } - } - } + if (!sizeof($samp)) + $this->_error('No such sample'); - function _remove_sample_from_group() { - if (!$this->has_arg('BLSAMPLEGROUPID')) $this->_error('No sample group specified'); - if (!$this->has_arg('BLSAMPLEGROUPSAMPLEID')) $this->_error('No group sample id specified'); + $this->db->pq("DELETE FROM blsamplegroup_has_blsample WHERE blsampleid = :1 AND blsamplegroupid = :2", array($blSampleGroupSampleId, $blSampleGroupId)); + } - $gid = $this->arg('BLSAMPLEGROUPID'); - $arr = explode('-', $this->arg('BLSAMPLEGROUPSAMPLEID')); - $sid = end($arr); + function _get_sample_group_samples_by_container() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - $this->_delete_sample_from_group($gid, $sid); + $args = array($this->proposalid); + $total_select_field = 'count(*) as total'; + $with_fields = ""; + $result_select_fields = ""; - $this->_output(new \stdClass); - } + $main_query = $this->_sample_groups_samples_by_container_main_query($with_fields, $result_select_fields); - function _delete_sample_from_group($blSampleGroupId, $blSampleGroupSampleId) { - $samp = $this->db->pq("SELECT b.blsampleid, pr.proteinid,cr.crystalid,dp.diffractionplanid - FROM blsample b - INNER JOIN crystal cr ON cr.crystalid = b.crystalid - INNER JOIN protein pr ON pr.proteinid = cr.proteinid - LEFT OUTER JOIN diffractionplan dp on dp.diffractionplanid = b.diffractionplanid - WHERE pr.proposalid = :1 AND b.blsampleid = :2", array($this->proposalid, $blSampleGroupSampleId)); + $total_query = "SELECT $total_select_field FROM ($main_query) group_containers"; + $total = $this->db->pq($total_query, $args); - if (!sizeof($samp)) $this->_error('No such sample'); - - $this->db->pq("DELETE FROM blsamplegroup_has_blsample WHERE blsampleid = :1 AND blsamplegroupid = :2", array($blSampleGroupSampleId, $blSampleGroupId)); - } - - // Get spacegroups from database - // Could extend this to filter on spacegroupshortname (also unique) - // Also extend to take mx used flag to return a filtered list - function _get_spacegroups() { - // Store the spacegroup id if we have one - $sid = $this->has_arg('SPACEGROUPID') ? $this->arg('SPACEGROUPID') : null; - // Reusing ty as a parameter. Set this to mx to only get those items in MX_used column - $mx = $this->has_arg('ty') ? $this->arg('ty') == 'mx' : null; - $where = "WHERE 1=1"; - $args = array(); - - // Are we looking for a specific id or subset of entries? - // Presence of id takes precedence over mx flag - if ($sid) { - $where .= ' AND spacegroupid = :'.(sizeof($args)+1); - array_push($args, $sid); - } else if ($mx) { - $where .= ' AND mx_used = :'.(sizeof($args)+1); - array_push($args, 1); - } + $total_result = intval($total[0]['TOTAL']); - $rows = $this->db->pq("SELECT spacegroupid, spacegroupnumber, spacegroupshortname, spacegroupname, bravaisLattice FROM spacegroup $where", $args); + $start = 0; + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $end = $pp; - if (!sizeof($rows)) { - if ($this->has_arg('SPACEGROUPID')) $this->_error('Spacegroup id not found'); - else $this->_error('No spacegroup types found'); - } + if ($this->has_arg('page')) { + $pg = $this->arg('page') - 1; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + } - if ($this->has_arg('SPACEGROUPID')) $this->_output($rows[0]); - else $this->_output(array('total' => count($rows), 'data' => $rows)); - } -} + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $with_fields .= "bsg.blsamplegroupid, bsg.name as sample_group_name, "; + $result_select_fields .= " + GROUP_CONCAT(DISTINCT(bsmp.blsampleid)) as blsampleids, + count(distinct (bsmp.blsampleid)) as total_samples, + GROUP_CONCAT(DISTINCT(smp.blsamplegroupid)) as sample_groups_ids, + GROUP_CONCAT(DISTINCT(smp.sample_group_name)) as sample_group_names, + "; + + $result_query = $this->_sample_groups_samples_by_container_main_query($with_fields, $result_select_fields); + $rows = $this->db->paginate($result_query, $args); + + $this->_output(array( + 'total' => $total_result, + 'data' => $rows, + )); + } + + // Get spacegroups from database + // Could extend this to filter on spacegroupshortname (also unique) + // Also extend to take mx used flag to return a filtered list + function _get_spacegroups() + { + // Store the spacegroup id if we have one + $sid = $this->has_arg('SPACEGROUPID') ? $this->arg('SPACEGROUPID') : null; + // Reusing ty as a parameter. Set this to mx to only get those items in MX_used column + $mx = $this->has_arg('ty') ? $this->arg('ty') == 'mx' : null; + $where = "WHERE 1=1"; + $args = array(); + + // Are we looking for a specific id or subset of entries? + // Presence of id takes precedence over mx flag + if ($sid) { + $where .= ' AND spacegroupid = :' . (sizeof($args) + 1); + array_push($args, $sid); + } else if ($mx) { + $where .= ' AND mx_used = :' . (sizeof($args) + 1); + array_push($args, 1); + } + + $rows = $this->db->pq("SELECT spacegroupid, spacegroupnumber, spacegroupshortname, spacegroupname, bravaisLattice FROM spacegroup $where", $args); + if (!sizeof($rows)) { + if ($this->has_arg('SPACEGROUPID')) + $this->_error('Spacegroup id not found'); + else + $this->_error('No spacegroup types found'); + } + + if ($this->has_arg('SPACEGROUPID')) + $this->_output($rows[0]); + else + $this->_output(array('total' => count($rows), 'data' => $rows)); + } + + function _sample_groups_samples_by_container_main_query($with_fields, $result_select_fields) + { + return " + WITH samples AS ( + SELECT $with_fields bshg.blsampleid + FROM blsamplegroup_has_blsample bshg + LEFT OUTER JOIN blsamplegroup bsg ON bsg.blsamplegroupid = bshg.blsamplegroupid WHERE bsg.proposalid =:1 + ) + SELECT $result_select_fields c.containerid FROM blsample bsmp + JOIN samples smp ON smp.blsampleid = bsmp.blsampleid + JOIN container c on bsmp.containerid = c.containerid + GROUP BY c.containerid + "; + } +} diff --git a/api/src/Page/Shipment.php b/api/src/Page/Shipment.php index 4003e8daa..8958261a6 100644 --- a/api/src/Page/Shipment.php +++ b/api/src/Page/Shipment.php @@ -2,277 +2,330 @@ namespace SynchWeb\Page; +use Exception; +use SynchWeb\Database\DatabaseQueryBuilder; use SynchWeb\Page; use SynchWeb\Shipment\Courier\DHL; +use SynchWeb\Shipment\ShippingService; use SynchWeb\Email; use SynchWeb\ImagingShared; +use SynchWeb\Shipment\ShippingService\ShippingService as ShippingServiceShippingService; + +use SynchWeb\Utils; class Shipment extends Page { - - - public static $arg_list = array('did' => '\d+', - 'cid' => '\d+', - 'sid' => '\d+', - 'lcid' => '\d+', - 'pid' => '\d+', - 'iid' => '\d+', - - - 'visit' => '\w+\d+-\d+', - 'bl' => '[\w-]+', - 'current' => '\d', - 'all' => '\d', - 'ty' => '\w+', - 'imager' => '\d', - 'imid' => '\d+', - - 'requestedimager' => '\d', - 'firstexperimentdate' => '\d\d-\d\d-\d\d\d\d', - - // cache name - 'name' => '\w+', - - - // Dewar Fields - 'CODE' => '([\w-])+', - 'FACILITYCODE' => '([\w-])+', - 'NEWFACILITYCODE' => '([\w-])+', - 'TRACKINGNUMBERTOSYNCHROTRON' => '\w+', - 'TRACKINGNUMBERFROMSYNCHROTRON' => '\w+', - 'FIRSTEXPERIMENTID' => '\d+|^(?![\s\S])', - 'SHIPPINGID' => '\d+', - - 'DEWARREGISTRYID' => '\d+', - - 'BARCODE' => '([\w-])+', - 'LOCATION' => '[\w|\s|-]+', - 'NEXTLOCATION' => '\w+|^(?![\s\S])', - 'STATUS' => '[\w|\s|-]+', - - 'PURCHASEDATE' => '\d+-\d+-\d+', - 'LABCONTACTID' => '\d+', - 'REPORT' => '.*', - - 'ADDRESS' => '.*', - 'DESCRIPTION' => '.*', - 'EMAILADDRESS' => '.*', - 'FAMILYNAME' => '.*', - 'GIVENNAME' => '.*', - 'LABNAME' => '.*', - 'LOCALCONTACT' => '[\w|\s+|-]+', - 'NEXTLOCALCONTACT' => '\w+|^(?![\s\S])', - 'PHONENUMBER' => '.*', - 'VISIT' => '\w+\d+-\d+', - 'NEXTVISIT' => '\w+\d+-\d+|^(?![\s\S])', - 'AWBNUMBER' => '\w+', - 'WEIGHT' => '\d+', - - // Shipment fields - 'FCODES' => '([\w-])+', - 'SENDINGLABCONTACTID' => '\d+', - 'RETURNLABCONTACTID' => '\d+', - 'SHIPPINGNAME' => '([\w\s-])+', - 'DELIVERYAGENT_SHIPPINGDATE' => '\d+-\d+-\d+', - 'DELIVERYAGENT_DELIVERYDATE' => '\d+-\d+-\d+', - 'DELIVERYAGENT_AGENTNAME' => '[\s|\w|-]+', - 'DELIVERYAGENT_AGENTCODE' => '[\w-]+', - 'SAFETYLEVEL' => '\w+', - 'DEWARS' => '\d+', - //'FIRSTEXPERIMENTID' => '\w+\d+-\d+', - 'COMMENTS' => '.*', - - 'assigned' => '\d', - 'bl' => '[\w-]+', - 'unassigned' => '[\w-]+', - - // Container fields - 'DEWARID' => '\d+', - 'CAPACITY' => '\d+', - 'CONTAINERTYPE' => '\w+', - 'NAME' => '([\w-])+', - 'SCHEDULEID' => '\d+', - 'SCREENID' => '\d+', - 'PERSONID' => '\d+', - 'DISPOSE' => '\d', - 'REQUESTEDRETURN' => '\d', - 'REQUESTEDIMAGERID' => '\d+', - 'CONTAINERID' => '\d+', - 'UNQUEUE' => '\d', - 'EXPERIMENTTYPE' => '\w+', - 'STORAGETEMPERATURE' => '[\w-]+', - 'AUTOMATED' => '\d+', - 'PUCK' => '\d', - 'PROCESSINGPIPELINEID' => '\d+', - 'OWNERID' => '\d+', - - 'CONTAINERREGISTRYID' => '\d+', - 'PROPOSALID' => '\d+', - 't' => '\w+', - - 'RETURN' => '\d+', - 'DECLAREDVALUE' => '\d+', - 'DESCRIPTION' => '.*', - 'DEWARS' => '\d+', - - 'PHYSICALLOCATION' => '[\s|\w|-]+', - 'READYBYTIME' => '\d\d:\d\d', - 'CLOSETIME' => '\d\d:\d\d', - 'PRODUCTCODE' => '\w', - 'BEAMLINENAME' => '[\w-]+', - - 'manifest' => '\d', - 'currentuser' => '\d', - - ); - - - public static $dispatch = array(array('/shipments(/:sid)', 'get', '_get_shipments'), - array('/shipments', 'post', '_add_shipment'), - array('/shipments/:sid', 'patch', '_update_shipment'), - array('/send/:sid', 'get', '_send_shipment'), - array('/countries', 'get', '_get_countries'), - - - array('/dewars(/:did)(/sid/:sid)(/fc/:FACILITYCODE)', 'get', '_get_dewars'), - array('/dewars', 'post', '_add_dewar'), - array('/dewars/:did', 'patch', '_update_dewar'), - array('/dewars/comments/:did', 'patch', '_update_dewar_comments'), // endpoint to allow bcr to update comments - - array('/dewars/history(/did/:did)', 'get', '_get_history'), - array('/dewars/history', 'post', '_add_history'), - - array('/dewars/registry/proposals', 'get', '_get_prop_dewar'), - array('/dewars/registry/proposals', 'post', '_add_prop_dewar'), - array('/dewars/registry/proposals/:DEWARREGISTRYHASPROPOSALID', 'delete', '_rem_prop_dewar'), - - array('/dewars/registry(/:FACILITYCODE)', 'get', '_dewar_registry'), - array('/dewars/registry/:FACILITYCODE', 'patch', '_update_dewar_registry'), - array('/dewars/registry/:FACILITYCODE', 'put', '_add_dewar_registry'), - - array('/dewars/reports(/:drid)', 'get', '_get_dewar_reports'), - array('/dewars/reports', 'post', '_add_dewar_report'), - - array('/dewars/default', 'get', '_get_default_dewar'), - - array('/dewars/transfer', 'post', '_transfer_dewar'), - array('/dewars/dispatch', 'post', '_dispatch_dewar'), - - array('/dewars/tracking(/:DEWARID)', 'get', '_get_dewar_tracking'), - - + public static $arg_list = array( + 'did' => '\d+', + 'cid' => '\d+', + 'sid' => '\d+', + 'lcid' => '\d+', + 'pid' => '\d+', + 'iid' => '\d+', + + + 'visit' => '\w+\d+-\d+', + 'current' => '\d', + 'all' => '\d', + 'ty' => '\w+', + 'imager' => '\d', + 'imid' => '\d+', + + 'requestedimager' => '\d', + 'firstexperimentdate' => '\d\d-\d\d-\d\d\d\d', + // cache name + 'name' => '\w+', + + // Dewar Fields + 'CODE' => '([\w\-])+', + 'FACILITYCODE' => '([\w\-])+', + 'NEWFACILITYCODE' => '([\w\-])+', + 'TRACKINGNUMBERTOSYNCHROTRON' => '\w*', + 'TRACKINGNUMBERFROMSYNCHROTRON' => '\w*', + 'FIRSTEXPERIMENTID' => '\d+|^(?![\s\S])', + 'SHIPPINGID' => '\d+', + + 'DEWARREGISTRYID' => '\d+', + + 'BARCODE' => '([\w\-])+', + 'LOCATION' => '[\w|\s|\-]+', + 'NEXTLOCATION' => '\w+|^(?![\s\S])', + 'STATUS' => '[\w|\s|\-]+', + + 'PURCHASEDATE' => '\d+-\d+-\d+', + 'LABCONTACTID' => '\d+', + 'REPORT' => '.*', + + 'ADDRESS' => '.*', + 'COUNTRY' => '.*', + 'CITY' => '([\w\s\-])+', + 'POSTCODE' => '([\w\s\-])+', + // 'DESCRIPTION' => '.*', + 'EMAILADDRESS' => '.*', + 'FAMILYNAME' => '.*', + 'GIVENNAME' => '.*', + 'LABNAME' => '.*', + 'LOCALCONTACT' => '[\w|\s+|\-]+', + 'NEXTLOCALCONTACT' => '\w+|^(?![\s\S])', + 'PHONENUMBER' => '.*', + 'VISIT' => '\w+\d+-\d+', + 'NEXTVISIT' => '\w+\d+-\d+|^(?![\s\S])', + 'AWBNUMBER' => '\w+', + 'WEIGHT' => '\d+', + + // Shipment fields + 'FCODES' => '([\w\-])+', + 'SENDINGLABCONTACTID' => '\d+', + 'RETURNLABCONTACTID' => '\d+', + 'SHIPPINGNAME' => '([\w\s\-])+', + 'DELIVERYAGENT_SHIPPINGDATE' => '\d+-\d+-\d+', + 'DELIVERYAGENT_DELIVERYDATE' => '\d+-\d+-\d+', + 'DELIVERYAGENT_AGENTNAME' => '[\s|\w|\-]+', + 'DELIVERYAGENT_AGENTCODE' => '[\w\-]+', + 'DELIVERYAGENT_FLIGHTCODE' => '\d*', + 'SAFETYLEVEL' => '\w+', + // 'DEWARS' => '\d+', + //'FIRSTEXPERIMENTID' => '\w+\d+-\d+', + + // Fields for responsive remote questions: + 'DYNAMIC' => '1?|Yes|No', + 'REMOTEORMAILIN' => '.*', + 'SESSIONLENGTH' => '\w+', + 'ENERGY' => '.*', + 'MICROFOCUSBEAM' => '1?|Yes|No', + 'SCHEDULINGRESTRICTIONS' => '.*', + 'LASTMINUTEBEAMTIME' => '1?|Yes|No', + 'DEWARGROUPING' => '.*', + 'EXTRASUPPORTREQUIREMENT' => '.*', + 'MULTIAXISGONIOMETRY' => '1?|Yes|No', + 'ENCLOSEDHARDDRIVE' => '1?|Yes|No', + 'ENCLOSEDTOOLS' => '1?|Yes|No', + + 'COMMENTS' => '.*', + + 'assigned' => '\d', + 'bl' => '[\w\-]+', + 'unassigned' => '[\w\-]+', + + // Container fields + 'DEWARID' => '\d+', + 'CAPACITY' => '\d+', + 'CONTAINERTYPE' => '([\w\-])+', + 'NAME' => '([\w\-])+', + 'SCHEDULEID' => '\d+', + 'SCREENID' => '\d+', + 'PERSONID' => '\d+', + 'DISPOSE' => '\d', + 'REQUESTEDRETURN' => '\d', + 'REQUESTEDIMAGERID' => '\d+', + 'CONTAINERID' => '\d+', + 'UNQUEUE' => '\d', + 'EXPERIMENTTYPE' => '\w+', + 'STORAGETEMPERATURE' => '[\w\-]+', + 'AUTOMATED' => '\d+', + 'PUCK' => '\d', + 'PROCESSINGPIPELINEID' => '\d+', + 'OWNERID' => '\d+', + + 'CONTAINERREGISTRYID' => '\d+', + 'PROPOSALID' => '\d+', + 't' => '\w+', + + 'RETURN' => '\d+', + 'DECLAREDVALUE' => '\d+', + 'DESCRIPTION' => '.*', + 'DEWARS' => '\d+', + + 'PHYSICALLOCATION' => '[\s|\w|\-]+', + 'READYBYTIME' => '\d\d:\d\d', + 'CLOSETIME' => '\d\d:\d\d', + 'PRODUCTCODE' => '\w', + 'BEAMLINENAME' => '[\w\-]+', + + 'manifest' => '\d', + 'currentuser' => '\d', + + ); + + var $extra_arg_list = array( + 'DYNAMIC', + 'REMOTEORMAILIN', + 'SESSIONLENGTH', + 'ENERGY', + 'MICROFOCUSBEAM', + 'SCHEDULINGRESTRICTIONS', + 'LASTMINUTEBEAMTIME', + 'DEWARGROUPING', + 'EXTRASUPPORTREQUIREMENT', + 'MULTIAXISGONIOMETRY', + 'ENCLOSEDHARDDRIVE', + 'ENCLOSEDTOOLS' + ); + + public static $dispatch = array( + array('/shipments(/:sid)', 'get', '_get_shipments'), + array('/shipments', 'post', '_add_shipment'), + array('/shipments/:sid', 'patch', '_update_shipment'), + array('/shipments/:sid', 'put', '_dummy_shipment_put'), + array('/send/:sid', 'get', '_send_shipment'), + array('/countries', 'get', '_get_countries'), + + + array('/dewars(/:did)(/sid/:sid)(/fc/:FACILITYCODE)', 'get', '_get_dewars'), + array('/dewars', 'post', '_add_dewar'), + array('/dewars/:did', 'patch', '_update_dewar'), + array('/dewars/comments/:did', 'patch', '_update_dewar_comments'), // endpoint to allow bcr to update comments + + array('/dewars/history(/did/:did)', 'get', '_get_history'), + array('/dewars/history', 'post', '_add_history'), + + array('/dewars/registry/proposals', 'get', '_get_prop_dewar'), + array('/dewars/registry/proposals', 'post', '_add_prop_dewar'), + array('/dewars/registry/proposals/:DEWARREGISTRYHASPROPOSALID', 'delete', '_rem_prop_dewar'), + + array('/dewars/registry(/:FACILITYCODE)', 'get', '_dewar_registry'), + array('/dewars/registry/:FACILITYCODE', 'patch', '_update_dewar_registry'), + array('/dewars/registry/:FACILITYCODE', 'put', '_add_dewar_registry'), + + array('/dewars/reports(/:drid)', 'get', '_get_dewar_reports'), + array('/dewars/reports', 'post', '_add_dewar_report'), + + array('/dewars/default', 'get', '_get_default_dewar'), + + array('/dewars/transfer', 'post', '_transfer_dewar'), + array('/dewars/dispatch', 'post', '_dispatch_dewar'), + + array('/dewars/tracking(/:DEWARID)', 'get', '_get_dewar_tracking'), + + + + array('/containers(/:cid)(/sid/:sid)(/did/:did)', 'get', '_get_all_containers'), + array('/containers', 'post', '_add_container'), + array('/containers/:cid', 'patch', '_update_container'), + array('/containers/move', 'get', '_move_container'), + + // TODO: Need to have a separate method for handling queueing and unqueueing of containers + array('/containers/queue(/:cid)', 'patch', '_queue_container'), + array('/containers/queue(/:cid)', 'get', '_queue_container'), + array('/containers/barcode/:BARCODE', 'get', '_check_container'), + + + array('/containers/registry(/:CONTAINERREGISTRYID)', 'get', '_container_registry'), + array('/containers/registry', 'post', '_add_container_registry'), + array('/containers/registry/:CONTAINERREGISTRYID', 'patch', '_update_container_registry'), + + array('/containers/registry/proposals', 'get', '_get_prop_container'), + array('/containers/registry/proposals', 'post', '_add_prop_container'), + array('/containers/registry/proposals/:CONTAINERREGISTRYHASPROPOSALID', 'delete', '_rem_prop_container'), - array('/containers(/:cid)(/did/:did)', 'get', '_get_all_containers'), - array('/containers/', 'post', '_add_container'), - array('/containers/:cid', 'patch', '_update_container'), - array('/containers/move', 'get', '_move_container'), + array('/containers/history', 'get', '_container_history'), + array('/containers/history', 'post', '_add_container_history'), + array('/containers/reports(/:CONTAINERREPORTID)', 'get', '_get_container_reports'), + array('/containers/reports', 'post', '_add_container_report'), + array('/containers/types', 'get', '_get_container_types'), - // TODO: Need to have a separate method for handling queueing and unqueueing of containers - array('/containers/queue(/:cid)', 'patch', '_queue_container'), - array('/containers/queue(/:cid)', 'get', '_queue_container'), - array('/containers/barcode/:BARCODE', 'get', '_check_container'), + array('/containers/notify/:BARCODE', 'get', '_notify_container'), + array('/cache/:name', 'put', '_session_cache'), + array('/cache/:name', 'get', '_get_session_cache'), - array('/containers/registry(/:CONTAINERREGISTRYID)', 'get', '_container_registry'), - array('/containers/registry', 'post', '_add_container_registry'), - array('/containers/registry/:CONTAINERREGISTRYID', 'patch', '_update_container_registry'), - array('/containers/registry/proposals', 'get', '_get_prop_container'), - array('/containers/registry/proposals', 'post', '_add_prop_container'), - array('/containers/registry/proposals/:CONTAINERREGISTRYHASPROPOSALID', 'delete', '_rem_prop_container'), + array('/terms/:sid', 'get', '_get_terms'), + array('/terms/:sid', 'patch', '_accept_terms'), - array('/containers/history', 'get', '_container_history'), - array('/containers/history', 'post', '_add_container_history'), - array('/containers/reports(/:CONTAINERREPORTID)', 'get', '_get_container_reports'), - array('/containers/reports', 'post', '_add_container_report'), - array('/containers/types', 'get', '_get_container_types'), - - array('/containers/notify/:BARCODE', 'get', '_notify_container'), + array('/awb/:sid', 'post', '_create_awb'), + array('/awb/quote', 'get', '_quote_awb'), - array('/cache/:name', 'put', '_session_cache'), - array('/cache/:name', 'get', '_get_session_cache'), + array('/pickup/:sid', 'post', '_rebook_pickup'), + array('/pickup/cancel/:sid', 'delete', '_cancel_pickup'), + ); + // Keep session open so we can cache data + var $session_close = False; + var $dhl = null; + var $shipping_service = null; - array('/terms/:sid', 'get', '_get_terms'), - array('/terms/:sid', 'patch', '_accept_terms'), + function __construct() + { + call_user_func_array(array('parent', '__construct'), func_get_args()); - array('/awb/:sid', 'post', '_create_awb'), - array('/awb/quote', 'get', '_quote_awb'), - - array('/pickup/:sid', 'post', '_rebook_pickup'), - array('/pickup/cancel/:sid', 'delete', '_cancel_pickup'), - ); - - // Keep session open so we can cache data - var $session_close = False; - - - function __construct() { - call_user_func_array(array('parent', '__construct'), func_get_args()); + global $dhl_user, $dhl_pass, $dhl_env; + $this->dhl = new DHL($dhl_user, $dhl_pass, $dhl_env); + $this->shipping_service = new ShippingService(); + } - global $dhl_user, $dhl_pass, $dhl_env; - $this->dhl = new DHL($dhl_user, $dhl_pass, $dhl_env); + // Get the args that will be passsed into the 'extra' JSON column of the Shipping table + function _get_extra_args() + { + $extra_args = array(); + foreach ($this->extra_arg_list as $arg) { + if ($this->has_arg($arg)) { + $extra_args[$arg] = $this->arg($arg); + } } + return $extra_args; + } + # ------------------------------------------------------------------------ + # List of shipments for a proposal + function _get_shipments() + { + if ($this->has_arg('all') && $this->user->hasPermission('view_manifest')) { + $args = array(); + $where = '1=1'; + } else { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified', 'Please select a proposal first'); + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; + } - - # ------------------------------------------------------------------------ - # List of shipments for a proposal - function _get_shipments() { - if ($this->has_arg('all') && $this->user->has('view_manifest')) { - $args = array(); - $where = '1=1'; - - } else { - if (!$this->has_arg('prop')) $this->_error('No proposal specified', 'Please select a proposal first'); - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; - } + if ($this->has_arg('sid')) { + $where .= ' AND s.shippingid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + } - if ($this->has_arg('sid')) { - $where .= ' AND s.shippingid=:'.(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } - - if ($this->has_arg('manifest')) { - $where .= ' AND s.deliveryagent_flightcodetimestamp is NOT NULL + if ($this->has_arg('manifest')) { + $where .= ' AND s.deliveryagent_flightcodetimestamp is NOT NULL AND d.deliveryagent_barcode IS NOT NULL'; - } + } - if ($this->has_arg('s')) { - $st = sizeof($args)+1; - $where .= " AND (lower(s.shippingname) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(s.shippingname) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')))"; + array_push($args, $this->arg('s')); + array_push($args, $this->arg('s')); + } - $tot = $this->db->pq("SELECT count(distinct s.shippingid) as tot + $tot = $this->db->pq("SELECT count(distinct s.shippingid) as tot FROM proposal p INNER JOIN shipping s ON p.proposalid = s.proposalid LEFT OUTER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid LEFT OUTER JOIN labcontact c2 ON s.returnlabcontactid = c2.labcontactid LEFT OUTER JOIN dewar d ON d.shippingid = s.shippingid WHERE $where", $args); - $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 's.creationdate DESC'; - if ($this->has_arg('sort_by')) { - $cols = array('SHIPPINGNAME' => 's.shippingname'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } + $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 's.creationdate DESC'; + if ($this->has_arg('sort_by')) { + $cols = array('SHIPPINGNAME' => 's.shippingname'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } - $rows = $this->db->paginate("SELECT s.deliveryagent_agentname, s.deliveryagent_agentcode, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as deliveryagent_shippingdate, TO_CHAR(s.deliveryagent_deliverydate, 'DD-MM-YYYY') as deliveryagent_deliverydate, s.safetylevel, count(d.dewarid) as dcount,s.sendinglabcontactid, c.cardname as lcout, c2.cardname as lcret, s.returnlabcontactid, s.shippingid, s.shippingname, s.shippingstatus,TO_CHAR(s.creationdate, 'DD-MM-YYYY') as created, s.isstorageshipping, s.shippingtype, s.comments, s.deliveryagent_flightcode, IF(s.deliveryAgent_label IS NOT NULL, 1, 0) as deliveryagent_has_label, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_readybytime, 'HH24:MI') as deliveryAgent_readybytime, TO_CHAR(s.deliveryAgent_callintime, 'HH24:MI') as deliveryAgent_callintime, CONCAT(p.proposalcode, p.proposalnumber) as prop, TO_CHAR(s.deliveryagent_flightcodetimestamp, 'HH24:MI DD-MM-YYYY') as deliveryagent_flightcodetimestamp, sum(d.weight) as weight, pe.givenname, pe.familyname, l.name as labname, l.address, l.city, l.postcode, l.country, CONCAT(p.proposalcode, p.proposalnumber) as prop, GROUP_CONCAT(IF(d.facilitycode, d.facilitycode, d.code)) as dewars, s.deliveryagent_productcode, IF(cta.couriertermsacceptedid,1,0) as termsaccepted, GROUP_CONCAT(d.deliveryagent_barcode) as deliveryagent_barcode, pe2.login as deliveryagent_flightcodeperson + $rows = $this->db->paginate("SELECT s.deliveryagent_agentname, s.deliveryagent_agentcode, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as deliveryagent_shippingdate, TO_CHAR(s.deliveryagent_deliverydate, 'DD-MM-YYYY') as deliveryagent_deliverydate, s.safetylevel, count(d.dewarid) as dcount,s.sendinglabcontactid, c.cardname as lcout, c2.cardname as lcret, s.returnlabcontactid, s.shippingid, s.shippingname, s.shippingstatus,TO_CHAR(s.creationdate, 'DD-MM-YYYY') as created, s.isstorageshipping, s.shippingtype, s.comments, s.deliveryagent_flightcode, IF(s.deliveryAgent_label IS NOT NULL, 1, 0) as deliveryagent_has_label, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_readybytime, 'HH24:MI') as deliveryAgent_readybytime, TO_CHAR(s.deliveryAgent_callintime, 'HH24:MI') as deliveryAgent_callintime, CONCAT(p.proposalcode, p.proposalnumber) as prop, TO_CHAR(s.deliveryagent_flightcodetimestamp, 'HH24:MI DD-MM-YYYY') as deliveryagent_flightcodetimestamp, sum(d.weight) as weight, pe.givenname, pe.familyname, l.name as labname, l.address, l.city, l.postcode, l.country, CONCAT(p.proposalcode, p.proposalnumber) as prop, GROUP_CONCAT(IF(d.facilitycode, d.facilitycode, d.code)) as dewars, s.deliveryagent_productcode, IF(cta.couriertermsacceptedid,1,0) as termsaccepted, GROUP_CONCAT(d.deliveryagent_barcode) as deliveryagent_barcode, pe2.login as deliveryagent_flightcodeperson, s.extra, + SUM(IF(d.dewarstatus = 'processing', 1, 0)) as processing FROM proposal p INNER JOIN shipping s ON p.proposalid = s.proposalid LEFT OUTER JOIN labcontact c2 ON s.returnlabcontactid = c2.labcontactid @@ -286,81 +339,98 @@ function _get_shipments() { GROUP BY s.sendinglabcontactid, s.returnlabcontactid, s.deliveryagent_agentname, s.deliveryagent_agentcode, s.deliveryagent_shippingdate, s.deliveryagent_deliverydate, s.safetylevel, c.cardname, c2.cardname, s.shippingid, s.shippingname, s.shippingstatus,TO_CHAR(s.creationdate, 'DD-MM-YYYY'), s.isstorageshipping, s.shippingtype, s.comments, s.creationdate ORDER BY $order", $args); - foreach ($rows as &$s) { - $s['DELIVERYAGENT_BARCODE'] = str_replace(',', ', ', $s['DELIVERYAGENT_BARCODE']); + foreach ($rows as &$s) { + $s['DELIVERYAGENT_BARCODE'] = str_replace(',', ', ', $s['DELIVERYAGENT_BARCODE']); + $extra_json = json_decode($s['EXTRA'], true); + if (is_null($extra_json)) { + $extra_json = array(); + foreach ($this->extra_arg_list as $arg) { + $extra_json[$arg] = ""; + } } - - if ($this->has_arg('sid')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such shipment'); - } else $this->_output(array( + $s = array_merge($s, $extra_json); + } + + if ($this->has_arg('sid')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such shipment'); + } else + $this->_output(array( 'total' => $tot, 'data' => $rows, )); - } - - - # ------------------------------------------------------------------------ - # Dewar history - function _get_history() { - if (!$this->has_arg('did') && !$this->has_arg('FACILITYCODE')) $this->_error('No dewar specified'); + } - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; - if ($this->has_arg('all') && ($this->bcr() || $this->staff)) { - $args = array(); - $where = '1=1'; - } - if ($this->has_arg('did')) { - $where .= ' AND d.dewarid=:'.(sizeof($args)+1); - array_push($args, $this->arg('did')); - } else { - $where .= ' AND d.facilitycode=:'.(sizeof($args)+1); - array_push($args, $this->arg('FACILITYCODE')); - } + # ------------------------------------------------------------------------ + # Dewar history + function _get_history() + { + if (!$this->has_arg('did') && !$this->has_arg('FACILITYCODE')) + $this->_error('No dewar specified'); + + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; + if ($this->has_arg('all') && ($this->bcr() || $this->staff)) { + $args = array(); + $where = '1=1'; + } + + if ($this->has_arg('did')) { + $where .= ' AND d.dewarid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('did')); + } else { + $where .= ' AND d.facilitycode=:' . (sizeof($args) + 1); + array_push($args, $this->arg('FACILITYCODE')); + } - $tot = $this->db->pq("SELECT count(h.dewartransporthistoryid) as tot + $tot = $this->db->pq("SELECT count(h.dewartransporthistoryid) as tot FROM dewartransporthistory h INNER JOIN dewar d ON d.dewarid = h.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT s.shippingid, s.shippingname as shipment, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), b.visit_number) as visit, b.beamlinename as bl, b.beamlineoperator as localcontact, h.dewarid, h.dewarstatus,h.storagelocation,TO_CHAR(h.arrivaldate, 'DD-MM-YYYY HH24:MI') as arrival, d.comments + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT s.shippingid, s.shippingname as shipment, CONCAT(p.proposalcode, p.proposalnumber, '-', b.visit_number) as visit, b.beamlinename as bl, b.beamlineoperator as localcontact, h.dewarid, h.dewarstatus,h.storagelocation,TO_CHAR(h.arrivaldate, 'DD-MM-YYYY HH24:MI') as arrival, d.comments FROM dewartransporthistory h INNER JOIN dewar d ON d.dewarid = h.dewarid INNER JOIN shipping s ON d.shippingid = s.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid LEFT OUTER JOIN blsession b ON b.sessionid = d.firstexperimentid WHERE $where ORDER BY h.arrivaldate DESC", $args); - - $this->_output(array('total' => $tot, 'data' => $rows)); - } + + $this->_output(array('total' => $tot, 'data' => $rows)); + } - function _add_history() { - global $in_contacts, $transfer_email; - global $dewar_complete_email; // Email list to cc if dewar back from beamline - # Flag to indicate we should e-mail users their dewar has returned from BL - $from_beamline = False; + function _add_history() + { + global $in_contacts, $arrival_email; + global $dewar_complete_email, $dewar_complete_email_locations; // Email list to cc if dewar back from beamline + # Flag to indicate we should e-mail users their dewar has returned from BL + $send_return_email = False; - if (!$this->bcr()) $this->_error('You need to be on the internal network to add history'); + if (!$this->bcr()) + $this->_error('You need to be on the internal network to add history'); - if (!$this->has_arg('BARCODE')) $this->_error('No barcode specified'); - if (!$this->has_arg('LOCATION')) $this->_error('No location specified'); + if (!$this->has_arg('BARCODE')) + $this->_error('No barcode specified'); + if (!$this->has_arg('LOCATION')) + $this->_error('No location specified'); - $dew = $this->db->pq("SELECT CONCAT(CONCAT(pe.givenname, ' '), pe.familyname) as lcout, pe.emailaddress as lcoutemail, CONCAT(CONCAT(pe2.givenname, ' '), pe2.familyname) as lcret, pe2.emailaddress as lcretemail, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), e.visit_number) as firstexp, TO_CHAR(e.startdate, 'DD-MM-YYYY HH24:MI') as firstexpst, e.beamlinename, e.beamlineoperator, d.dewarid, d.trackingnumberfromsynchrotron, s.shippingid, s.shippingname, p.proposalcode, CONCAT(p.proposalcode, p.proposalnumber) as prop, d.barcode, d.facilitycode, d.firstexperimentid, d.dewarstatus + $dew = $this->db->pq("SELECT CONCAT(pe.givenname, ' ', pe.familyname) as lcout, pe.emailaddress as lcoutemail, CONCAT(CONCAT(pe2.givenname, ' '), pe2.familyname) as lcret, pe2.emailaddress as lcretemail, CONCAT(p.proposalcode, p.proposalnumber, '-', e.visit_number) as firstexp, TO_CHAR(e.startdate, 'DD-MM-YYYY HH24:MI') as firstexpst, e.beamlinename, e.beamlineoperator, d.dewarid, d.trackingnumberfromsynchrotron, s.shippingid, s.shippingname, p.proposalcode, CONCAT(p.proposalcode, p.proposalnumber) as prop, d.barcode, d.facilitycode, d.firstexperimentid, d.dewarstatus FROM dewar d INNER JOIN shipping s ON s.shippingid = d.shippingid LEFT OUTER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid @@ -371,96 +441,92 @@ function _add_history() { LEFT OUTER JOIN blsession e ON e.sessionid = d.firstexperimentid WHERE lower(barcode) LIKE lower(:1)", array($this->arg('BARCODE'))); - if (!sizeof($dew)) $this->_error('No such dewar'); - else $dew = $dew[0]; + if (!sizeof($dew)) + $this->_error('No such dewar'); + else + $dew = $dew[0]; - $track = $this->has_arg('TRACKINGNUMBERFROMSYNCHROTRON') ? $this->arg('TRACKINGNUMBERFROMSYNCHROTRON') : $dew['TRACKINGNUMBERFROMSYNCHROTRON']; + $track = $this->has_arg('TRACKINGNUMBERFROMSYNCHROTRON') ? $this->arg('TRACKINGNUMBERFROMSYNCHROTRON') : $dew['TRACKINGNUMBERFROMSYNCHROTRON']; - // What was the last history entry for this dewar? - // If it's come from a beamline, register flag so we can e-mail further down... - $last_history_results = $this->db->pq("SELECT storageLocation FROM dewartransporthistory WHERE dewarId = :1 ORDER BY DewarTransportHistoryId DESC LIMIT 1", array($dew['DEWARID'])); + // What was the last history entry for this dewar? + // If it's come from a beamline, register flag so we can e-mail further down... + $last_history_results = $this->db->pq("SELECT storageLocation FROM dewartransporthistory WHERE dewarId = :1 ORDER BY DewarTransportHistoryId DESC LIMIT 1", array($dew['DEWARID'])); - if (sizeof($last_history_results)) { - $last_history = $last_history_results[0]; - // We only add data to dewar history in lower case from this method. - // If that ever changes, update this to become case insensitive search - $last_location = $last_history['STORAGELOCATION']; - - // Why don't we have beamline names in the database...? - // Currently grabbing them from the config object - // Not particularly efficient, but this is not a time critical operation so - // this approach covers all beamlines for future proofing. - // Stop/break if we find a match + if (sizeof($last_history_results)) { + $last_history = $last_history_results[0]; + // We only add data to dewar history in lower case from this method. + // If that ever changes, update this to become case insensitive search + $last_location = $last_history['STORAGELOCATION']; + if (!isset($dewar_complete_email_locations) || !is_array($dewar_complete_email_locations)) { $bls = $this->_get_beamlines_from_type('all'); + $send_return_email = in_array($last_location, $bls); + } else if (array_key_exists($last_location, $dewar_complete_email_locations)) { + $email_location = $dewar_complete_email_locations[$last_location]; + $send_return_email = preg_match($email_location, strtolower($this->arg('LOCATION'))); + } + } else { + // No history - could be a new dewar, so not necessarily an error... + if ($this->debug) + error_log("No previous dewar transport history for DewarId " . $dew['DEWARID']); + } + // If dewar status is dispatch-requested - don't change it. + // This way the dewar can be moved between storage locations and keep the record that a user requested the dispatch + // Default status is 'at-facility' + $dewarstatus = strtolower($dew['DEWARSTATUS']) == 'dispatch-requested' ? 'dispatch-requested' : 'at facility'; - if (in_array($last_location, $bls)) { - $from_beamline = True; - } - } else { - // No history - could be a new dewar, so not necessarily an error... - if ($this->debug) error_log("No previous dewar transport history for DewarId ". $dew['DEWARID']); - } - // If dewar status is dispatch-requested - don't change it. - // This way the dewar can be moved between storage locations and keep the record that a user requested the dispatch - // Default status is 'at-facility' - $dewarstatus = strtolower($dew['DEWARSTATUS']) == 'dispatch-requested' ? 'dispatch-requested' : 'at facility'; - - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) VALUES (s_dewartransporthistory.nextval,:1,lower(:2),lower(:3),CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", array($dew['DEWARID'], $dewarstatus, $this->arg('LOCATION'))); - $dhid = $this->db->id(); + $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) VALUES (s_dewartransporthistory.nextval,:1,lower(:2),lower(:3),CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", array($dew['DEWARID'], $dewarstatus, $this->arg('LOCATION'))); + $dhid = $this->db->id(); - $this->db->pq("UPDATE dewar set dewarstatus=lower(:4), storagelocation=lower(:2), trackingnumberfromsynchrotron=:3 WHERE dewarid=:1", array($dew['DEWARID'], $this->arg('LOCATION'), $track, $dewarstatus)); - $this->db->pq("UPDATE shipping set shippingstatus=lower(:2) WHERE shippingid=:1", array($dew['SHIPPINGID'], $dewarstatus)); + $this->db->pq("UPDATE dewar set dewarstatus=lower(:4), storagelocation=lower(:2), trackingnumberfromsynchrotron=:3 WHERE dewarid=:1", array($dew['DEWARID'], $this->arg('LOCATION'), $track, $dewarstatus)); + $this->db->pq("UPDATE shipping set shippingstatus=lower(:2) WHERE shippingid=:1", array($dew['SHIPPINGID'], $dewarstatus)); - $containers = $this->db->pq("SELECT containerid + $containers = $this->db->pq("SELECT containerid FROM container WHERE dewarid=:1", array($dew['DEWARID'])); - foreach ($containers as $c) { - $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,:2)", array($c['CONTAINERID'], 'at facility')); - } + foreach ($containers as $c) { + $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,:2)", array($c['CONTAINERID'], 'at facility')); + } - // Email - // EHCs, local contact(s), labcontact, dh, pa - $dew['NOW'] = strftime('%d-%m-%Y %H:%M'); - $dew['INCONTACTS'] = $in_contacts; - $dew['TRACKINGNUMBERFROMSYNCHROTRON'] = $track; + // Email + // EHCs, local contact(s), labcontact, dh, pa + $dew['NOW'] = strftime('%d-%m-%Y %H:%M'); + $dew['INCONTACTS'] = $in_contacts; + $dew['TRACKINGNUMBERFROMSYNCHROTRON'] = $track; - if (strtolower($this->arg('LOCATION')) == 'stores-in' && $dew['LCOUTEMAIL']) { - $lcs = $this->db->pq("SELECT p.login + if (strtolower($this->arg('LOCATION')) == 'stores-in' && $dew['LCOUTEMAIL']) { + $lcs = $this->db->pq("SELECT p.login FROM person p INNER JOIN session_has_person shp ON shp.personid = p.personid WHERE shp.sessionid=:1 AND (shp.role = 'Local Contact' OR shp.role = 'Local Contact 2')", array($dew['FIRSTEXPERIMENTID'])); - $emails = array($dew['LCOUTEMAIL'],$transfer_email); - foreach ($lcs as $lc) { - array_push($emails, $this->_get_email($lc['LOGIN'])); - } - - $email = new Email($dew['PROPOSALCODE'] == 'in' ? 'dewar-stores-in-in' : 'dewar-stores-in', '*** Dewar Received for '.$dew['PROP'].' at '.$dew['NOW'].' ***'); - $email->data = $dew; - $email->send(implode(', ', $emails)); + $emails = array($dew['LCOUTEMAIL'], $arrival_email); + foreach ($lcs as $lc) { + array_push($emails, $this->_get_email($lc['LOGIN'])); } - if (strtolower($this->arg('LOCATION')) == 'stores-out' && $dew['LCRETEMAIL']) { - $email = new Email('dewar-stores-out', '*** Dewar ready to leave Synchrotron ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL']); - } + $email = new Email($dew['PROPOSALCODE'] == 'in' ? 'dewar-stores-in-in' : 'dewar-stores-in', '*** Dewar Received for ' . $dew['PROP'] . ' at ' . $dew['NOW'] . ' ***'); + $email->data = $dew; + $email->send(implode(', ', $emails)); + } - if (strpos(strtolower($this->arg('LOCATION')),'-rack') !== false && $dew['LCRETEMAIL']) { - $dew['LOCATION'] = $this->arg('LOCATION'); + if (strtolower($this->arg('LOCATION')) == 'stores-out' && $dew['LCRETEMAIL']) { + $email = new Email('dewar-stores-out', '*** Dewar ready to leave Synchrotron ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL']); + } - $email = new Email('dewar-rack', '*** Dewar now outside Beamline ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL']); - } + if (strpos(strtolower($this->arg('LOCATION')), '-rack') !== false && $dew['LCRETEMAIL']) { + $dew['LOCATION'] = $this->arg('LOCATION'); - // Change this so it checks if the boolean flag "from_beamline" is set - // The old version assumed rack--from-bl - //if (preg_match('/rack-\w+-from-bl/', strtolower($this->arg('LOCATION'))) && $dew['LCRETEMAIL']) { - if ($from_beamline && $dew['LCRETEMAIL']) { - // Any data collections for this dewar's containers? - // Note this counts data collection ids for containers and uses the DataCollection.SESSIONID to determine the session/visit - // Should work for UDC (where container.sessionid is set) as well as any normal scheduled session (where container.sessionid is not set) - $rows = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, dc.sessionid, count(dc.datacollectionid) as dccount + $email = new Email('dewar-rack', '*** Dewar now outside Beamline ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL']); + } + + if ($dew['LCRETEMAIL'] && $send_return_email) { + // Any data collections for this dewar's containers? + // Note this counts data collection ids for containers and uses the DataCollection.SESSIONID to determine the session/visit + // Should work for UDC (where container.sessionid is set) as well as any normal scheduled session (where container.sessionid is not set) + $rows = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, dc.sessionid, count(dc.datacollectionid) as dccount FROM Dewar d INNER JOIN Container c on c.dewarid = d.dewarid INNER JOIN BLSample bls ON bls.containerid = c.containerid @@ -470,79 +536,94 @@ function _add_history() { WHERE d.dewarid = :1 GROUP BY dc.sessionid", array($dew['DEWARID'])); - if (sizeof($rows)) $dew['DC'] = $rows; + if (sizeof($rows)) + $dew['DC'] = $rows; - $cc = $dewar_complete_email ? $dewar_complete_email : null; - // Log the event if debugging - if ($this->debug) error_log("Dewar " . $dew['DEWARID'] . " back from beamline..."); + $cc = array($dewar_complete_email ? $dewar_complete_email : null); - $email = new Email('storage-rack', '*** Visit finished, dewar awaiting instructions ***'); - $email->data = $dew; - $email->send($dew['LCRETEMAIL'], $cc); + $owners = $this->db->pq("SELECT p.emailaddress + FROM Container c + INNER JOIN Person p ON c.ownerId = p.personId + WHERE c.dewarId = :1 + GROUP BY p.emailaddress", array($dew['DEWARID'])); + + foreach ($owners as $owner) { + if ($owner['EMAILADDRESS'] != '') array_push($cc, $owner['EMAILADDRESS']); } - $this->_output(array('DEWARHISTORYID' => $dhid)); + // Log the event if debugging + if ($this->debug) error_log("Dewar " . $dew['DEWARID'] . " back from beamline..."); + + $email = new Email('storage-rack', '*** Visit finished, dewar awaiting instructions ***'); + $email->data = $dew; + $email->send($dew['LCRETEMAIL'], implode(', ', $cc)); } + $this->_output(array('DEWARHISTORYID' => $dhid)); + } - function _dewar_registry() { - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; - $fields = "r.dewarregistryid, max(CONCAT(p.proposalcode, p.proposalnumber)) as prop, r.facilitycode, TO_CHAR(r.purchasedate, 'DD-MM-YYYY') as purchasedate, ROUND(TIMESTAMPDIFF('DAY',r.purchasedate, CURRENT_TIMESTAMP)/30.42,1) as age, r.labcontactid, count(d.dewarid) as dewars, GROUP_CONCAT(distinct CONCAT(p.proposalcode,p.proposalnumber) SEPARATOR ', ') as proposals, r.bltimestamp, TO_CHAR(max(d.bltimestamp),'DD-MM-YYYY') as lastuse, count(dr.dewarreportid) as reports"; - $group = "r.facilitycode"; + function _dewar_registry() + { + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; - if ($this->has_arg('all') && $this->staff) { - $args = array(); - $where = '1=1'; - } + $fields = "r.dewarregistryid, max(CONCAT(p.proposalcode, p.proposalnumber)) as prop, r.facilitycode, TO_CHAR(r.purchasedate, 'DD-MM-YYYY') as purchasedate, ROUND(TIMESTAMPDIFF('DAY',r.purchasedate, CURRENT_TIMESTAMP)/30.42,1) as age, r.labcontactid, count(distinct d.dewarid) as dewars, GROUP_CONCAT(distinct CONCAT(p.proposalcode,p.proposalnumber) SEPARATOR ', ') as proposals, r.bltimestamp, TO_CHAR(max(d.bltimestamp),'DD-MM-YYYY') as lastuse, count(dr.dewarreportid) as reports"; + $group = "r.facilitycode"; - if ($this->has_arg('FACILITYCODE')) { - $where .= ' AND r.facilitycode=:'.(sizeof($args)+1); - array_push($args, $this->arg('FACILITYCODE')); - } + if ($this->has_arg('all') && $this->staff) { + $args = array(); + $where = '1=1'; + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(r.facilitycode) LIKE lower(CONCAT(CONCAT('%', :".($st)."), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:".($st + 1)."), '%')))"; - array_push($args, $this->arg('s'), $this->arg('s')); - } + if ($this->has_arg('FACILITYCODE')) { + $where .= ' AND r.facilitycode=:' . (sizeof($args) + 1); + array_push($args, $this->arg('FACILITYCODE')); + } - if ($this->has_arg('t')) { - if ($this->arg('t') == 'orphan') $where .= " AND rhp.dewarregistryid IS NULL"; - } + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(r.facilitycode) LIKE lower(CONCAT(CONCAT('%', :" . ($st) . "), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')))"; + array_push($args, $this->arg('s'), $this->arg('s')); + } + + if ($this->has_arg('t')) { + if ($this->arg('t') == 'orphan') + $where .= " AND rhp.dewarregistryid IS NULL"; + } - $tot = $this->db->pq("SELECT count(r.facilitycode) as tot + $tot = $this->db->pq("SELECT count(r.facilitycode) as tot FROM dewarregistry r LEFT OUTER JOIN dewarregistry_has_proposal rhp ON r.dewarregistryid = rhp.dewarregistryid LEFT OUTER JOIN proposal p ON p.proposalid = r.proposalid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'r.facilitycode'; - if ($this->has_arg('sort_by')) { - $cols = array( - 'FACILITYCODE' => 'r.facilitycode', 'DEWARS' => 'count(distinct d.dewarid)', - 'LASTUSE' => 'max(d.bltimestamp)', 'BLTIMESTAMP' => 'r.bltimestamp', - 'REPORTS' => 'count(dr.dewarreportid)' - ); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'r.facilitycode'; + if ($this->has_arg('sort_by')) { + $cols = array( + 'FACILITYCODE' => 'r.facilitycode', 'DEWARS' => 'count(distinct d.dewarid)', + 'LASTUSE' => 'max(d.bltimestamp)', 'BLTIMESTAMP' => 'r.bltimestamp', + 'REPORTS' => 'count(dr.dewarreportid)' + ); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } - $rows = $this->db->paginate("SELECT $fields + $rows = $this->db->paginate("SELECT $fields FROM dewarregistry r LEFT OUTER JOIN dewarregistry_has_proposal rhp ON r.dewarregistryid = rhp.dewarregistryid LEFT OUTER JOIN dewarreport dr ON r.facilitycode = dr.facilitycode @@ -553,67 +634,78 @@ function _dewar_registry() { WHERE $where GROUP BY $group ORDER BY $order", $args); - - if ($this->has_arg('FACILITYCODE')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such dewar'); - - } else $this->_output(array('total' => $tot, 'data' => $rows)); - } - + if ($this->has_arg('FACILITYCODE')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such dewar'); + } else + $this->_output(array('total' => $tot, 'data' => $rows)); + } - function _add_dewar_registry() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('FACILITYCODE')) $this->_error('No dewar code specified'); - $fc = strtoupper($this->arg('FACILITYCODE')); - $check = $this->db->pq("SELECT dewarregistryid FROM dewarregistry WHERE facilitycode=:1", array($fc)); - if (sizeof($check)) { - $this->_error('That facility code is already in use'); - } + function _add_dewar_registry() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('FACILITYCODE')) + $this->_error('No dewar code specified'); - $purchase = $this->has_arg('PURCHASEDATE') ? $this->arg('PURCHASEDATE') : ''; - $this->db->pq("INSERT INTO dewarregistry (facilitycode, purchasedate, bltimestamp) VALUES (:1, TO_DATE(:2, 'DD-MM-YYYY'), SYSDATE)", array($fc, $purchase)); + $fc = strtoupper($this->arg('FACILITYCODE')); + $check = $this->db->pq("SELECT dewarregistryid FROM dewarregistry WHERE facilitycode=:1", array($fc)); - $this->_output(array('FACILITYCODE' => $fc, 'DEWARREGISTRYID' => $this->db->id())); + if (sizeof($check)) { + $this->_error('That facility code is already in use'); } + $purchase = $this->has_arg('PURCHASEDATE') ? $this->arg('PURCHASEDATE') : ''; + $this->db->pq("INSERT INTO dewarregistry (facilitycode, purchasedate, bltimestamp) VALUES (:1, TO_DATE(:2, 'DD-MM-YYYY'), SYSDATE)", array($fc, $purchase)); - function _update_dewar_registry() { - if (!$this->has_arg('FACILITYCODE')) $this->_error('No dewar code specified'); + $this->_output(array('FACILITYCODE' => $fc, 'DEWARREGISTRYID' => $this->db->id())); + } - $dew = $this->db->pq("SELECT facilitycode FROM dewarregistry WHERE facilitycode LIKE :1 AND proposalid = :2", array($this->arg('FACILITYCODE'), $this->proposalid)); - if (!sizeof($dew)) $this->_error('No such dewar'); - else $dew = $dew[0]; + function _update_dewar_registry() + { + if (!$this->has_arg('FACILITYCODE')) + $this->_error('No dewar code specified'); - $fields = array('PURCHASEDATE'); - if ($this->staff) array_push($fields, 'NEWFACILITYCODE'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $fl = ':1'; - if (in_array($f, array('PURCHASEDATE'))) { - $fl = "TO_DATE(:1, 'DD-MM-YYYY')"; - } + $dew = $this->db->pq("SELECT facilitycode FROM dewarregistry WHERE facilitycode LIKE :1 AND proposalid = :2", array($this->arg('FACILITYCODE'), $this->proposalid)); - if ($f == 'NEWFACILITYCODE') { - $this->db->pq("UPDATE dewarregistry SET FACILITYCODE=$fl WHERE facilitycode=:2", array($this->arg($f), $this->arg('FACILITYCODE'))); - $this->_output(array('FACILITYCODE' => $this->arg($f))); + if (!sizeof($dew)) + $this->_error('No such dewar'); + else + $dew = $dew[0]; - } else { - $this->db->pq("UPDATE dewarregistry SET $f=$fl WHERE facilitycode=:2", array($this->arg($f), $this->arg('FACILITYCODE'))); - $this->_output(array($f => $this->arg($f))); - } + $fields = array('PURCHASEDATE'); + if ($this->staff) + array_push($fields, 'NEWFACILITYCODE'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $fl = ':1'; + if (in_array($f, array('PURCHASEDATE'))) { + $fl = "TO_DATE(:1, 'DD-MM-YYYY')"; + } + + if ($f == 'NEWFACILITYCODE') { + $this->db->pq("UPDATE dewarregistry SET FACILITYCODE=$fl WHERE facilitycode=:2", array($this->arg($f), $this->arg('FACILITYCODE'))); + $this->_output(array('FACILITYCODE' => $this->arg($f))); + } else { + $this->db->pq("UPDATE dewarregistry SET $f=$fl WHERE facilitycode=:2", array($this->arg($f), $this->arg('FACILITYCODE'))); + $this->_output(array($f => $this->arg($f))); } } } + } - function _get_prop_dewar() { - if (!$this->has_arg('DEWARREGISTRYID')) $this->_error('No dewar specified'); + function _get_prop_dewar() + { + if (!$this->has_arg('DEWARREGISTRYID')) + $this->_error('No dewar specified'); - $rows = $this->db->pq("SELECT drhp.dewarregistryhasproposalid,drhp.dewarregistryid,drhp.proposalid,CONCAT(p.proposalcode, p.proposalnumber) as proposal, pe.familyname, pe.givenname, pe.phonenumber, pe.emailaddress, lc.cardname, l.name as labname, l.address, l.city, l.postcode, l.country + $rows = $this->db->pq("SELECT drhp.dewarregistryhasproposalid,drhp.dewarregistryid,drhp.proposalid,CONCAT(p.proposalcode, p.proposalnumber) as proposal, pe.familyname, pe.givenname, pe.phonenumber, pe.emailaddress, lc.cardname, l.name as labname, l.address, l.city, l.postcode, l.country FROM dewarregistry_has_proposal drhp INNER JOIN proposal p ON p.proposalid = drhp.proposalid LEFT OUTER JOIN labcontact lc ON drhp.labcontactid = lc.labcontactid @@ -622,72 +714,86 @@ function _get_prop_dewar() { WHERE drhp.dewarregistryid = :1", array($this->arg('DEWARREGISTRYID'))); - $this->_output($rows); - } - - function _add_prop_dewar() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('DEWARREGISTRYID')) $this->_error('No dewar specified'); - if (!$this->has_arg('PROPOSALID')) $this->_error('No proposal specified'); - if (!$this->has_arg('LABCONTACTID')) $this->_error('No lab contact specified'); + $this->_output($rows); + } - $chk = $this->db->pq("SELECT dewarregistryid + function _add_prop_dewar() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('DEWARREGISTRYID')) + $this->_error('No dewar specified'); + if (!$this->has_arg('PROPOSALID')) + $this->_error('No proposal specified'); + if (!$this->has_arg('LABCONTACTID')) + $this->_error('No lab contact specified'); + + $chk = $this->db->pq("SELECT dewarregistryid FROM dewarregistry_has_proposal WHERE dewarregistryid = :1 AND proposalid = :2", array($this->arg('DEWARREGISTRYID'), $this->arg('PROPOSALID'))); - if (sizeof($chk)) $this->_error('That dewar is already registered to that proposal'); + if (sizeof($chk)) + $this->_error('That dewar is already registered to that proposal'); - $this->db->pq("INSERT INTO dewarregistry_has_proposal (dewarregistryid,proposalid,personid,labcontactid) - VALUES (:1,:2,:3,:4)", array($this->arg('DEWARREGISTRYID'), $this->arg('PROPOSALID'), $this->user->personid, $this->arg('LABCONTACTID'))); + $this->db->pq("INSERT INTO dewarregistry_has_proposal (dewarregistryid,proposalid,personid,labcontactid) + VALUES (:1,:2,:3,:4)", array($this->arg('DEWARREGISTRYID'), $this->arg('PROPOSALID'), $this->user->personId, $this->arg('LABCONTACTID'))); - $this->_output(array('DEWARREGISTRYHASPROPOSALID' => $this->db->id())); - } + $this->_output(array('DEWARREGISTRYHASPROPOSALID' => $this->db->id())); + } - function _rem_prop_dewar() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('DEWARREGISTRYHASPROPOSALID')) $this->_error('No dewar proposal specified'); + function _rem_prop_dewar() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('DEWARREGISTRYHASPROPOSALID')) + $this->_error('No dewar proposal specified'); - $this->db->pq("DELETE FROM dewarregistry_has_proposal WHERE dewarregistryhasproposalid=:1", array($this->arg('DEWARREGISTRYHASPROPOSALID'))); - } + $this->db->pq("DELETE FROM dewarregistry_has_proposal WHERE dewarregistryhasproposalid=:1", array($this->arg('DEWARREGISTRYHASPROPOSALID'))); + } - function _get_dewar_reports() { - if (!$this->has_arg('FACILITYCODE')) $this->_error('No dewar specified'); + function _get_dewar_reports() + { + if (!$this->has_arg('FACILITYCODE')) + $this->_error('No dewar specified'); - $where = 'r.facilitycode=:1'; - $args = array($this->arg('FACILITYCODE')); + $where = 'r.facilitycode=:1'; + $args = array($this->arg('FACILITYCODE')); - $tot = $this->db->pq("SELECT count(r.dewarreportid) as tot + $tot = $this->db->pq("SELECT count(r.dewarreportid) as tot FROM dewarreport r WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT r.dewarreportid, r.report, TO_CHAR(r.bltimestamp, 'HH24:MI DD-MM-YYYY') as bltimestamp, r.attachment + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT r.dewarreportid, r.report, TO_CHAR(r.bltimestamp, 'HH24:MI DD-MM-YYYY') as bltimestamp, r.attachment FROM dewarreport r WHERE $where ORDER BY r.bltimestamp DESC", $args); - foreach ($rows as $i => &$row) { - $row['REPORT'] = $this->db->read($row['REPORT']); - } - - $this->_output(array('total' => $tot, 'data' => $rows)); + foreach ($rows as $i => &$row) { + $row['REPORT'] = $this->db->read($row['REPORT']); } - function _add_dewar_report() { - if (!$this->has_arg('REPORT')) $this->_error('No report specified'); - if (!$this->has_arg('FACILITYCODE')) $this->_error('No dewar specified'); + $this->_output(array('total' => $tot, 'data' => $rows)); + } + + function _add_dewar_report() + { + if (!$this->has_arg('REPORT')) + $this->_error('No report specified'); + if (!$this->has_arg('FACILITYCODE')) + $this->_error('No dewar specified'); - $last_visits = $this->db->pq("SELECT s.beamlineoperator as localcontact, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename + $last_visits = $this->db->pq("SELECT s.beamlineoperator as localcontact, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename FROM dewar d INNER JOIN blsession s ON d.firstexperimentid = s.sessionid INNER JOIN shipping sh ON sh.shippingid = d.shippingid @@ -695,801 +801,1058 @@ function _add_dewar_report() { WHERE d.facilitycode = :1 ORDER BY s.startdate DESC", array($this->arg('FACILITYCODE'))); - if (!sizeof($last_visits)) $this->_error('Cant find a visit for that dewar'); - else $lv = $last_visits[0]; + if (!sizeof($last_visits)) + $this->_error('Cant find a visit for that dewar'); + else + $lv = $last_visits[0]; - if (array_key_exists('ATTACHMENT', $_FILES)) { - if ($_FILES['ATTACHMENT']['name']) { - $info = pathinfo($_FILES['ATTACHMENT']['name']); + if (array_key_exists('ATTACHMENT', $_FILES)) { + if ($_FILES['ATTACHMENT']['name']) { + $info = pathinfo($_FILES['ATTACHMENT']['name']); - if ($info['extension'] == 'jpg') { - # dls_mxweb cant write to visits... - #$root = '/dls/'.$lv['BEAMLINENAME'].'/data/'.$lv['YEAR'].'/'.$lv['VISIT'].'/.ispyb/'; + if ($info['extension'] == 'jpg') { + # dls_mxweb cant write to visits... + #$root = '/dls/'.$lv['BEAMLINENAME'].'/data/'.$lv['YEAR'].'/'.$lv['VISIT'].'/.ispyb/'; - $root = '/dls_sw/dasc/ispyb2/uploads/'.$lv['YEAR'].'/'.$lv['VISIT'].'/'; - if (!is_dir($root)) { - mkdir($root, 0755, true); - } + $root = '/dls_sw/dasc/ispyb2/uploads/' . $lv['YEAR'] . '/' . $lv['VISIT'] . '/'; + if (!is_dir($root)) { + mkdir($root, 0755, true); + } - $file = strftime('%Y-%m-%d_%H%M').'dewarreport.jpg'; + $file = strftime('%Y-%m-%d_%H%M') . 'dewarreport.jpg'; - $this->db->pq("INSERT INTO dewarreport (dewarreportid,facilitycode,report,attachment,bltimestamp) VALUES (s_dewarreport.nextval,:1,:2,:3,SYSDATE) RETURNING dewarreportid INTO :id", - array($this->arg('FACILITYCODE'), $this->arg('REPORT'), $root.$file)); - move_uploaded_file($_FILES['ATTACHMENT']['tmp_name'], $root.$file); + $this->db->pq( + "INSERT INTO dewarreport (dewarreportid,facilitycode,report,attachment,bltimestamp) VALUES (s_dewarreport.nextval,:1,:2,:3,SYSDATE) RETURNING dewarreportid INTO :id", + array($this->arg('FACILITYCODE'), $this->arg('REPORT'), $root . $file) + ); + move_uploaded_file($_FILES['ATTACHMENT']['tmp_name'], $root . $file); - $lc = $this->db->pq("SELECT p.emailaddress + $lc = $this->db->pq("SELECT p.emailaddress FROM dewarregistry r INNER JOIN labcontact l ON l.labcontactid = r.labcontactid INNER JOIN person p ON p.personid = l.personid WHERE r.facilitycode=:1", array($this->arg('FACILITYCODE'))); - if (sizeof($lc)) { - $recpts = array($lc[0]['EMAILADDRESS']); - $local = $this->_get_email_fn($lv['LOCALCONTACT']); - if ($local) array_push($recpts, $local); - - $this->args['NOW'] = strftime('%d-%m-%Y %H:%M'); - $email = new Email('dewar-report', '*** Status Report for Dewar '.$this->arg('FACILITYCODE').' at '.$this->arg('NOW').' ***'); - $email->data = $this->args; - $email->send(implode(', ', $recpts)); - } + if (sizeof($lc)) { + $recpts = array($lc[0]['EMAILADDRESS']); + $local = $this->_get_email_fn($lv['LOCALCONTACT']); + if ($local) + array_push($recpts, $local); - - $this->_output(array('DEWARREPORTID' => $this->db->id())); + $this->args['NOW'] = strftime('%d-%m-%Y %H:%M'); + $email = new Email('dewar-report', '*** Status Report for Dewar ' . $this->arg('FACILITYCODE') . ' at ' . $this->arg('NOW') . ' ***'); + $email->data = $this->args; + $email->send(implode(', ', $recpts)); + } - } + $this->_output(array('DEWARREPORTID' => $this->db->id())); } } } + } - function _transfer_dewar() { - global $transfer_email; - if (!$this->has_arg('DEWARID')) $this->_error('No dewar specified'); + function _transfer_dewar() + { + global $transfer_email; + if (!$this->has_arg('DEWARID')) + $this->_error('No dewar specified'); + if (!$this->has_arg('LOCATION')) + $this->_error('No location specified'); - $dew = $this->db->pq("SELECT d.dewarid,s.shippingid + $dew = $this->db->pq("SELECT d.dewarid,s.shippingid FROM dewar d INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE d.dewarid=:1 and p.proposalid=:2", array($this->arg('DEWARID'), $this->proposalid)); - if (!sizeof($dew)) $this->_error('No such dewar'); - else $dew = $dew[0]; + if (!sizeof($dew)) + $this->_error('No such dewar'); + else + $dew = $dew[0]; - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) + $this->db->pq( + "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) VALUES (s_dewartransporthistory.nextval,:1,'transfer-requested',:2,CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", - array($dew['DEWARID'], $this->arg('LOCATION'))); + array($dew['DEWARID'], $this->arg('LOCATION')) + ); - // Update dewar status to transfer-requested to keep consistent with history - $this->db->pq("UPDATE dewar set dewarstatus='transfer-requested' WHERE dewarid=:1", array($dew['DEWARID'])); + // Update dewar status to transfer-requested to keep consistent with history + $this->db->pq("UPDATE dewar set dewarstatus='transfer-requested' WHERE dewarid=:1", array($dew['DEWARID'])); - if ($this->has_arg('NEXTVISIT')) { - $sessions = $this->db->pq("SELECT s.sessionid + if ($this->has_arg('NEXTVISIT')) { + $sessions = $this->db->pq( + "SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE p.proposalid=:1 AND CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :2", - array($this->proposalid, $this->arg('NEXTVISIT'))); + array($this->proposalid, $this->arg('NEXTVISIT')) + ); - $sessionId = !empty($sessions) ? $sessions[0]['SESSIONID'] : NULL; + $sessionId = !empty($sessions) ? $sessions[0]['SESSIONID'] : NULL; - $this->db->pq("UPDATE dewar SET firstexperimentid=:1 WHERE dewarid=:2", array($sessionId, $dew['DEWARID'])); - } + $this->db->pq("UPDATE dewar SET firstexperimentid=:1 WHERE dewarid=:2", array($sessionId, $dew['DEWARID'])); + } + $email = new Email('dewar-transfer', '*** Dewar ready for internal transfer ***'); + $nextLocalContact = $this->arg('NEXTLOCALCONTACT'); + $newContact = !empty($nextLocalContact) ? $nextLocalContact : $this->arg('LOCALCONTACT'); + $nextLocation = $this->arg('NEXTLOCATION'); - $email = new Email('dewar-transfer', '*** Dewar ready for internal transfer ***'); + $this->args['LCEMAIL'] = $this->_get_email_fn($this->arg('LOCALCONTACT')); + $this->args['LCNEXTEMAIL'] = $this->_get_email_fn($newContact); + $this->args['NEXTLOCATION'] = !empty($nextLocation) ? $nextLocation : $this->arg('LOCATION'); - $nextLocalContact = $this->arg('NEXTLOCALCONTACT'); - $newContact = !empty($nextLocalContact) ? $nextLocalContact : $this->arg('LOCALCONTACT'); - $nextLocation = $this->arg('NEXTLOCATION'); + $data = $this->args; + if (!array_key_exists('FACILITYCODE', $data)) + $data['FACILITYCODE'] = ''; + $email->data = $data; + $recpts = $transfer_email . ', ' . $this->arg('EMAILADDRESS'); + if ($this->args['LCEMAIL']) + $recpts .= ', ' . $this->arg('LCEMAIL'); + if ($this->args['LCNEXTEMAIL']) + $recpts .= ', ' . $this->arg('LCNEXTEMAIL'); - $this->args['LCEMAIL'] = $this->_get_email_fn($this->arg('LOCALCONTACT')); - $this->args['LCNEXTEMAIL'] = $this->_get_email_fn($newContact); - $this->args['NEXTLOCATION'] = !empty($nextLocation) ? $nextLocation : $this->arg('LOCATION'); + $email->send($recpts); - $data = $this->args; - if (!array_key_exists('FACILITYCODE', $data)) $data['FACILITYCODE'] = ''; - $email->data = $data; + $this->_output(1); + } - $recpts = $transfer_email.', '.$this->arg('EMAILADDRESS'); - if ($this->args['LCEMAIL']) $recpts .= ', '.$this->arg('LCEMAIL'); - if ($this->args['LCNEXTEMAIL']) $recpts .= ', '.$this->arg('LCNEXTEMAIL'); + function _dispatch_dewar_in_shipping_service($dispatch_info, $dewar) + { + global $facility_company; + global $facility_address; + global $facility_city; + global $facility_postcode; + global $facility_country; + global $facility_phone; + global $facility_contact; + global $shipping_service_api_url; + global $facility_email; + if (!isset($shipping_service_api_url)) { + throw new Exception("Could not send request to shipping service: shipping_service_api_url not set"); + } - $email->send($recpts); + # Create shipment + $shipment_data = array( + "consignee_company_name" => $dispatch_info['LABNAME'], + "consignee_country" => $dispatch_info['COUNTRY'], + "consignee_city" => $dispatch_info['CITY'], + "consignee_post_code" => Utils::getValueOrDefault($dispatch_info['POSTCODE'], null), + "consignee_contact_name" => $dispatch_info['GIVENNAME'] . " " . $dispatch_info['FAMILYNAME'], + "consignee_contact_phone_number" => $dispatch_info['PHONENUMBER'], + "consignee_contact_email" => $dispatch_info['EMAILADDRESS'], + "shipper_company_name" => $facility_company, + "shipper_address_line1" => $facility_address, + "shipper_city" => $facility_city, + "shipper_country" => $facility_country, + "shipper_post_code" => $facility_postcode, + "shipper_contact_name" => $facility_contact, + "shipper_contact_phone_number" => $facility_phone, + "shipper_contact_email" => $facility_email, + "internal_contact_name" => $this->has_arg('LOCALCONTACT') ? $this->args['LOCALCONTACT'] : null, + "shipment_reference" => $dispatch_info['VISIT'], + "external_id" => (int) $dispatch_info['DEWARID'], + "journey_type" => ShippingService::JOURNEY_FROM_FACILITY, + "packages" => array(array("external_id" => (int) $dispatch_info['DEWARID'])) + ); - $this->_output(1); + # Split up address. Necessary as address is a single field in ispyb + $address_lines = explode(PHP_EOL, rtrim($dispatch_info['ADDRESS'])); + $num_lines = count($address_lines); + if ($num_lines > 3) { + throw new Exception("Could not build request for shipping service: address input contains more than 3 lines (exc. city and post code)"); } + if (isset($address_lines[0])) $shipment_data['consignee_address_line1'] = $address_lines[0]; + if (isset($address_lines[1])) $shipment_data['consignee_address_line2'] = $address_lines[1]; + if (isset($address_lines[2])) $shipment_data['consignee_address_line3'] = $address_lines[2]; + $create = ($dewar['DEWARSTATUS'] != 'dispatch-requested'); - function _dispatch_dewar() { - global $dispatch_email; - // Variable to store where the dewar is (Synchrotron or eBIC building) - // Could map this to dewar storage locations in ISPyB to make more generic...? - $dispatch_from_location = 'Synchrotron'; + try { + if ($create === true) { + $shipment_data["proposal"] = $dewar["PROPOSAL"]; + $response = $this->shipping_service->create_shipment($shipment_data); + } else { + $this->shipping_service->update_shipment($dispatch_info['DEWARID'], $shipment_data, ShippingService::JOURNEY_FROM_FACILITY); + $response = $this->shipping_service->get_shipment($dispatch_info['DEWARID'], ShippingService::JOURNEY_FROM_FACILITY); + } + $shipment_id = $response['shipmentId']; + $this->shipping_service->dispatch_shipment($shipment_id, false); + } catch (Exception $e) { + throw new Exception("Error returned from shipping service: " . $e . "\nShipment data: " . json_encode($shipment_data)); + } - if (!$this->has_arg('DEWARID')) $this->_error('No dewar specified'); + return $shipment_id; + } - $dew = $this->db->pq("SELECT d.dewarid, d.barcode, d.storagelocation, s.shippingid - FROM dewar d - INNER JOIN shipping s ON s.shippingid = d.shippingid - INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE d.dewarid=:1 and p.proposalid=:2", array($this->arg('DEWARID'), $this->proposalid)); + function _dispatch_dewar() + { + global $facility_country; + global $facility_courier_countries; + global $dispatch_email; + global $dispatch_email_intl; + global $use_shipping_service; + global $shipping_service_links_in_emails; + // Variable to store where the dewar is (Synchrotron or eBIC building) + // Could map this to dewar storage locations in ISPyB to make more generic...? + $dispatch_from_location = 'Synchrotron'; + + if (!$this->has_arg('DEWARID')) + $this->_error('No dewar specified'); + + $country = $this->arg('COUNTRY'); + + if (!$this->arg('COUNTRY')) { + $this->_error('Error submitting country: ' . $country); + } - if (!sizeof($dew)) $this->_error('No such dewar'); - else $dew = $dew[0]; - - // If dewar is from eBIC, we want to update the e-mail subect line... - // Location is provided on form. - // If no location specified (i.e. deleted), then read from dewar transport history. - // If no dewar transport history fall back to dewar location - // We still update history based on provided location to record action from user - $dewar_location = $this->arg('LOCATION'); - - if (empty($dewar_location)) { - // What was the last history entry for this dewar? - // User may have accidentally removed location from form - $last_history_results = $this->db->pq("SELECT storageLocation FROM dewartransporthistory WHERE dewarId = :1 ORDER BY DewarTransportHistoryId DESC LIMIT 1", array($dew['DEWARID'])); - - if (sizeof($last_history_results)) { - $last_history = $last_history_results[0]; - - $dewar_location = $last_history['STORAGELOCATION']; - } else { - // Use the current location of the dewar instead if no history - $dewar_location = $dew['STORAGELOCATION']; - } - } - // Check if the last history storage location is an EBIC prefix or not - // Case insensitive search - if (stripos($dewar_location, 'ebic') !== false) { - $dispatch_from_location = 'eBIC'; + $dew = $this->db->pq( + "SELECT d.dewarid, d.barcode, d.storagelocation, d.dewarstatus, s.shippingid, CONCAT(p.proposalcode, p.proposalnumber) as proposal + FROM dewar d + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN proposal p ON p.proposalid = s.proposalid + WHERE d.dewarid=:1 and p.proposalid=:2", + array($this->arg('DEWARID'), $this->proposalid) + ); + + if (!sizeof($dew)) + $this->_error('No such dewar'); + else + $dew = $dew[0]; + + // If dewar is from eBIC, we want to update the e-mail subect line... + // Location is provided on form. + // If no location specified (i.e. deleted), then read from dewar transport history. + // If no dewar transport history fall back to dewar location + // We still update history based on provided location to record action from user + $dewar_location = $this->has_arg('LOCATION)') ? $this->arg('LOCATION') : ""; + + if (empty($dewar_location)) { + // What was the last history entry for this dewar? + // User may have accidentally removed location from form + $last_history_results = $this->db->pq( + "SELECT storageLocation + FROM dewartransporthistory + WHERE dewarId = :1 + ORDER BY DewarTransportHistoryId DESC + LIMIT 1", + array($dew['DEWARID']) + ); + + if (sizeof($last_history_results)) { + $last_history = $last_history_results[0]; + + $dewar_location = $last_history['STORAGELOCATION']; + } else { + // Use the current location of the dewar instead if no history + $dewar_location = Utils::getValueOrDefault($dew['STORAGELOCATION'], ''); } + } + // Check if the last history storage location is an EBIC prefix or not + // Case insensitive search + if (stripos($dewar_location, 'ebic') !== false) { + $dispatch_from_location = 'eBIC'; + } + + // Update dewar transport history with provided location. + $this->db->pq( + "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) + VALUES (s_dewartransporthistory.nextval,:1,'dispatch-requested',:2,CURRENT_TIMESTAMP) + RETURNING dewartransporthistoryid INTO :id", + array($dew['DEWARID'], $dewar_location) + ); - // Update dewar transport history with provided location. - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) - VALUES (s_dewartransporthistory.nextval,:1,'dispatch-requested',:2,CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", - array($dew['DEWARID'], $this->arg('LOCATION'))); + $terms = $this->db->pq( + "SELECT cta.couriertermsacceptedid FROM couriertermsaccepted cta WHERE cta.shippingid=:1", + array($dew['SHIPPINGID']) + ); + $terms_accepted = sizeof($terms) ? true : false; - // Also update the dewar status and storage location to keep it in sync with history... - $this->db->pq("UPDATE dewar set dewarstatus='dispatch-requested', storagelocation=lower(:2) WHERE dewarid=:1", array($dew['DEWARID'], $this->arg('LOCATION'))); + $data = $this->args; + $data['TERMSACCEPTED'] = $terms_accepted; - # Prepare e-mail response for dispatch request - $subject_line = '*** Dispatch requested for Dewar '.$dew['BARCODE'].' from '.$dispatch_from_location.' - Pickup Date: '.$this->args['DELIVERYAGENT_SHIPPINGDATE'].' ***'; - $email = new Email('dewar-dispatch', $subject_line); + if (Utils::getValueOrDefault($use_shipping_service) && in_array($country, $facility_courier_countries)) { + if ($terms_accepted) { + try { + $shipment_id = $this->_dispatch_dewar_in_shipping_service($data, $dew); + if (Utils::getValueOrDefault($shipping_service_links_in_emails)) { + $data['AWBURL'] = $this->shipping_service->get_awb_pdf_url($shipment_id); + } + } catch (Exception $e) { + error_log($e); + $data['AWBURL'] = ""; + } + } + } - $this->args['LCEMAIL'] = $this->_get_email_fn($this->arg('LOCALCONTACT')); + # Prepare e-mail response for dispatch request + $subject_line = '*** Dispatch requested for Dewar ' . $dew['BARCODE'] . ' from ' . $dispatch_from_location . ' - Pickup Date: ' . $this->args['DELIVERYAGENT_SHIPPINGDATE'] . ' ***'; + $email = new Email('dewar-dispatch', $subject_line); - // LDAP email search does not always provide a match - // So look at the ISPyB person record for a matching staff user - if (!$this->args['LCEMAIL'] && $this->args['LOCALCONTACT']) { - $this->args['LCEMAIL'] = $this->_get_ispyb_email_fn($this->args['LOCALCONTACT']); + // If a local contact is given, try to find their email address + // First try LDAP, if unsuccessful look at the ISPyB person record for a matching staff user + $local_contact = $this->has_arg('LOCALCONTACT') ? $this->args['LOCALCONTACT'] : ''; + if ($local_contact) { + $this->args['LCEMAIL'] = $this->_get_email_fn($local_contact); + if (!$this->args['LCEMAIL']) { + $this->args['LCEMAIL'] = $this->_get_ispyb_email_fn($local_contact); } + } - $data = $this->args; - if (!array_key_exists('FACILITYCODE', $data)) $data['FACILITYCODE'] = ''; - if (!array_key_exists('AWBNUMBER', $data)) $data['AWBNUMBER'] = ''; - if (!array_key_exists('DELIVERYAGENT_AGENTCODE', $data)) $data['DELIVERYAGENT_AGENTCODE'] = ''; - $email->data = $data; + if (!array_key_exists('FACILITYCODE', $data)) + $data['FACILITYCODE'] = ''; + if (!array_key_exists('AWBNUMBER', $data)) + $data['AWBNUMBER'] = ''; + if (!array_key_exists('DELIVERYAGENT_AGENTNAME', $data)) + $data['DELIVERYAGENT_AGENTNAME'] = ''; + if (!array_key_exists('DELIVERYAGENT_AGENTCODE', $data)) + $data['DELIVERYAGENT_AGENTCODE'] = ''; + if (!array_key_exists('LOCATION', $data)) + $data['LOCATION'] = $dewar_location; + if (!array_key_exists('LOCALCONTACT', $data)) + $data['LOCALCONTACT'] = $local_contact; + if (!array_key_exists('LCEMAIL', $data)) + $data['LCEMAIL'] = ''; + $email->data = $data; + + if ($country != $facility_country && !is_null($dispatch_email_intl)) { + $recpts = $dispatch_email_intl; + } else { + $recpts = $dispatch_email; + } - $recpts = $dispatch_email.', '.$this->arg('EMAILADDRESS'); - if ($this->args['LCEMAIL']) $recpts .= ', '.$this->args['LCEMAIL']; + $recpts .= ', ' . $this->arg('EMAILADDRESS'); + $local_contact_email = $this->has_arg('LCEMAIL') ? $this->args['LCEMAIL'] : ''; + if ($local_contact_email) $recpts .= ', ' . $local_contact_email; - $email->send($recpts); + $email->send($recpts); - $this->_output(1); + // Also update the dewar status and storage location to keep it in sync with history... + $this->db->pq( + "UPDATE dewar + set dewarstatus='dispatch-requested', storagelocation=lower(:2) + WHERE dewarid=:1", + array($dew['DEWARID'], $dewar_location) + ); + + $this->_output(1); + } + + + + function _get_dewar_tracking() + { + if (!$this->has_arg('prop')) { + $this->haltIfLackingPermission('all_dewars'); } + if (!$this->has_arg('DEWARID')) + $this->_error('No dewar specified'); + + if ($this->user->hasPermission('all_dewars')) { + $where = "d.dewarid=:1"; + $args = array($this->arg('DEWARID')); + } else { + $where = "d.dewarid=:1 AND p.proposalid=:2"; + $args = array($this->arg('DEWARID'), $this->proposalid); + } + $dewar = $this->db->pq( + "SELECT d.trackingnumbertosynchrotron,d.trackingnumberfromsynchrotron, LOWER(s.deliveryagent_agentname) as deliveryagent_agentname + FROM dewar d + INNER JOIN shipping s ON s.shippingid = d.shippingid + INNER JOIN proposal p ON p.proposalid = s.proposalid + WHERE $where", + $args + ); - function _get_dewar_tracking() { - if (!$this->has_arg('prop') && !$this->user->can('all_dewars')) $this->_error('No proposal id specified'); - if (!$this->has_arg('DEWARID')) $this->_error('No dewar specified'); + if (!sizeof($dewar)) + $this->_error('No such dewar'); + else + $dewar = $dewar[0]; - $where = 'AND p.proposalid=:1'; - $args = array($this->arg('DEWARID'), $this->proposalid); + $tracking_number_to_synchrotron = (string)($dewar['TRACKINGNUMBERTOSYNCHROTRON']); + $tracking_number_from_synchrotron = (string)($dewar['TRACKINGNUMBERFROMSYNCHROTRON']); - if ($this->user->can('all_dewars')) { - $where = ''; - $args = array($this->arg('DEWARID')); - } + $tracking_number = ($tracking_number_from_synchrotron) ? $tracking_number_from_synchrotron : $tracking_number_to_synchrotron; - $dew = $this->db->pq("SELECT d.trackingnumbertosynchrotron,d.trackingnumberfromsynchrotron, LOWER(s.deliveryagent_agentname) as deliveryagent_agentname - FROM dewar d - INNER JOIN shipping s ON s.shippingid = d.shippingid - INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE d.dewarid=:1 $where", $args); + if (!$tracking_number) $this->_error('Can\'t find tracking number for DewarId=' . $this->arg('DEWARID')); - if (!sizeof($dew)) $this->_error('No such dewar'); - else $dew = $dew[0]; + $delivery_agent = $dewar['DELIVERYAGENT_AGENTNAME']; - if ($dew['DELIVERYAGENT_AGENTNAME'] == 'dhl' - && (($dew['TRACKINGNUMBERTOSYNCHROTRON'] && strlen($dew['TRACKINGNUMBERTOSYNCHROTRON']) <= 10) || - ($dew['TRACKINGNUMBERFROMSYNCHROTRON'] && strlen($dew['TRACKINGNUMBERFROMSYNCHROTRON']) <= 10)) - ) { - $tr = $this->_dewar_tracking($dew); + if ($delivery_agent == 'dhl') { + $tracking_history = $this->_dhl_dewar_tracking($tracking_number); + $this->_output($tracking_history); + } else { + $this->_error('Can\'t get tracking history for this delivery agent: ' . $delivery_agent . PHP_EOL . 'Tracking is only currently supported for DHL'); + } + } - $this->_output(array( - 'ORIGIN' => (string)$tr['status']->AWBInfo->ShipmentInfo->OriginServiceArea->Description, - 'DESTINATION' => (string)$tr['status']->AWBInfo->ShipmentInfo->DestinationServiceArea->Description, - 'EVENTS' => $tr['events'] - )); + function _dhl_dewar_tracking($tracking_number) + { + $parsed_tracking_numbers = array(); + $lpnumber_matched = preg_match('/J?(JD\d+)/', $tracking_number, $parsed_tracking_numbers); + $awb_matched = ($lpnumber_matched) ? false : preg_match('/(\d{10})/', $tracking_number, $parsed_tracking_numbers); - } else { - $this->_output(); - } + if (!$awb_matched && !$lpnumber_matched) { + $this->_error('Tracking number \'' . $tracking_number . '\' doesn\'t satisfy DHL requirements'); } - function _dewar_tracking($dewar) { - if ($dewar['TRACKINGNUMBERFROMSYNCHROTRON']) $status = $this->dhl->get_tracking_info(array('AWB' => $dewar['TRACKINGNUMBERFROMSYNCHROTRON'])); - else $status = $this->dhl->get_tracking_info(array('AWB' => (string)($dewar['TRACKINGNUMBERTOSYNCHROTRON']))); - - if ($status->Response->Status) $this->_error($status->Response->Status); - else { - if ($status->AWBInfo->Status->ActionStatus != 'success') $this->_error((string)$status->AWBInfo->Status->ActionStatus); - else { - $events = array(); - // print_r($status->AWBInfo->ShipmentInfo); - $i = 1; - foreach ($status->AWBInfo->ShipmentInfo->ShipmentEvent as $e) { - $st = (string)$e->ServiceEvent->EventCode; - $event = array( - 'EVENTID' => $i++, - 'STISO' => (string)$e->Date.'T'.(string)$e->Time, - 'DATE' => (string)$e->Date.' '.(string)$e->Time, - 'EVENT' => (string)$e->ServiceEvent->EventCode, - 'STATE' => $this->dhl->tracking_status($st), - 'LOCATION' => (string)$e->ServiceArea->Description, - 'SIGNATORY' => (string)$e->Signatory - ); - - array_push($events, $event); - } + $tracking_number = $parsed_tracking_numbers[1]; - return array('status' => $status, 'events' => $events); - } - } + if ($lpnumber_matched) { + $status = $this->dhl->get_tracking_info(array('LPNumber' => $tracking_number)); + } else if ($awb_matched) { + $status = $this->dhl->get_tracking_info(array('AWB' => $tracking_number)); } - - # ------------------------------------------------------------------------ - # List of dewars for a shipment - function _get_dewars() { - if (!$this->has_arg('prop') && !$this->user->has('all_dewars')) $this->_error('No proposal id specified'); + if ($status->Response->Status) { + $this->_error($status->Response->Status); + } + if ($status->AWBInfo->Status->ActionStatus != 'success') { + $this->_error('Bad DHL action status: ' . (string)$status->AWBInfo->Status->ActionStatus); + } - $where = 's.proposalid=:1'; - $args = array($this->proposalid); + $events = array(); + $i = 1; + foreach ($status->AWBInfo->ShipmentInfo->ShipmentEvent as $event) { + $event_code = (string)$event->ServiceEvent->EventCode; + $event_date = (string)$event->Date; + $event_time = (string)$event->Time; + $event = array( + 'EVENTID' => $i++, + 'STISO' => (string)$event_date . 'T' . $event_time, + 'DATE' => (string)$event_date . ' ' . $event_time, + 'EVENT' => $event_code, + 'STATE' => $this->dhl->tracking_status($event_code), + 'LOCATION' => (string)$event->ServiceArea->Description, + 'SIGNATORY' => (string)$event->Signatory + ); + array_push($events, $event); + } - if ($this->user->has('all_dewars') && $this->has_arg('all')) { - $where = '1=1'; - $args = array(); - } + return array( + 'ORIGIN' => (string)$status->AWBInfo->ShipmentInfo->OriginServiceArea->Description, + 'DESTINATION' => (string)$status->AWBInfo->ShipmentInfo->DestinationServiceArea->Description, + 'EVENTS' => $events + ); + } - if ($this->has_arg('visit')) { - $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)=:".(sizeof($args)+1); - array_push($args, $this->arg('visit')); - } - if ($this->has_arg('bl')) { - $where .= ' AND se.beamlinename=:'.(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } - if ($this->has_arg('did')) { - $where .= ' AND d.dewarid=:'.(sizeof($args)+1); - array_push($args, $this->arg('did')); - } + # ------------------------------------------------------------------------ + # List of dewars for a shipment + function _get_dewars() + { + if (!$this->has_arg('prop') && !$this->user->hasPermission('all_dewars')) + $this->_error('No proposal id specified'); - if ($this->has_arg('FACILITYCODE')) { - $where .= ' AND d.facilitycode=:'.(sizeof($args)+1); - array_push($args, $this->arg('FACILITYCODE')); - } + $where = 's.proposalid=:1'; + $args = array($this->proposalid); - if ($this->has_arg('sid')) { - $where .= ' AND d.shippingid=:'.(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } + if ($this->user->hasPermission('all_dewars') && $this->has_arg('all')) { + $where = '1=1'; + $args = array(); + } - if ($this->has_arg('current')) { - $where .= ' AND (se.startdate > CURRENT_TIMESTAMP)'; - } + if ($this->has_arg('visit')) { + $where .= " AND CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)=:" . (sizeof($args) + 1); + array_push($args, $this->arg('visit')); + } - if ($this->has_arg('ty')) { - $bls_tmp = $this->_get_beamlines_from_type($this->arg('ty')); - if (!empty($bls_tmp)) { - $bls = implode("', '", $bls_tmp); - $where .= " AND se.beamlinename IN ('$bls')"; - } - } + if ($this->has_arg('bl')) { + $where .= ' AND se.beamlinename=:' . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } - if ($this->has_arg('requestedimager')) { - $where .= ' AND c.requestedimagerid IS NOT NULL'; - } + if ($this->has_arg('did')) { + $where .= ' AND d.dewarid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('did')); + } - if ($this->has_arg('firstexperimentdate')) { - $where .= " AND DATE(se.startdate) = TO_DATE(:".(sizeof($args)+1).", 'DD-MM-YYYY')"; - array_push($args, $this->arg('firstexperimentdate')); - } + if ($this->has_arg('FACILITYCODE')) { + $where .= ' AND d.facilitycode=:' . (sizeof($args) + 1); + array_push($args, $this->arg('FACILITYCODE')); + } + + if ($this->has_arg('sid')) { + $where .= ' AND d.shippingid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('sid')); + } + if ($this->has_arg('current')) { + $where .= ' AND (se.startdate > CURRENT_TIMESTAMP)'; + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(d.code) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) OR lower(d.facilitycode) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) OR lower(CONCAT(p.proposalcode, p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')))"; - for ($i = 0; $i < 3; $i++) array_push($args, $this->arg('s')); + if ($this->has_arg('ty')) { + $bls_tmp = $this->_get_beamlines_from_type($this->arg('ty')); + if (!empty($bls_tmp)) { + $bls = implode("', '", $bls_tmp); + $where .= " AND se.beamlinename IN ('$bls')"; } + } + + if ($this->has_arg('requestedimager')) { + $where .= ' AND c.requestedimagerid IS NOT NULL'; + } + + if ($this->has_arg('firstexperimentdate')) { + $where .= " AND DATE(se.startdate) = TO_DATE(:" . (sizeof($args) + 1) . ", 'DD-MM-YYYY')"; + array_push($args, $this->arg('firstexperimentdate')); + } + - $tot = $this->db->pq("SELECT count(distinct d.dewarid) as tot + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(d.code) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(d.facilitycode) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) OR lower(CONCAT(p.proposalcode, p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')))"; + for ($i = 0; $i < 3; $i++) + array_push($args, $this->arg('s')); + } + + $tot = $this->db->pq("SELECT count(distinct d.dewarid) as tot FROM dewar d LEFT OUTER JOIN container c ON c.dewarid = d.dewarid INNER JOIN shipping s ON d.shippingid = s.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid LEFT OUTER JOIN blsession se ON d.firstexperimentid = se.sessionid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'd.dewarid DESC'; - if ($this->has_arg('sort_by')) { - $cols = array('FIRSTEXPERIMENTST' => 'se.startdate'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'd.dewarid DESC'; + if ($this->has_arg('sort_by')) { + $cols = array('FIRSTEXPERIMENTST' => 'se.startdate'); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + + $dewars = $this->db->paginate("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number) as firstexperiment, r.labcontactid, se.beamlineoperator as localcontact, se.beamlinename, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY') as firstexperimentst, d.firstexperimentid, s.shippingid, s.shippingname, d.facilitycode, count(c.containerid) as ccount, (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end) as exp, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, s.deliveryagent_agentname, d.weight, d.deliveryagent_barcode, GROUP_CONCAT(c.code SEPARATOR ', ') as containers, s.sendinglabcontactid, s.returnlabcontactid, pe.givenname, pe.familyname FROM dewar d LEFT OUTER JOIN container c ON c.dewarid = d.dewarid INNER JOIN shipping s ON d.shippingid = s.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid LEFT OUTER JOIN blsession se ON d.firstexperimentid = se.sessionid LEFT OUTER JOIN dewarregistry r ON r.facilitycode = d.facilitycode + LEFT OUTER JOIN labcontact lc ON s.sendinglabcontactid = lc.labcontactid + LEFT OUTER JOIN person pe ON lc.personid = pe.personid WHERE $where - GROUP BY CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), se.visit_number), r.labcontactid, se.beamlineoperator, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY'), (case when se.visit_number > 0 then (CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), se.visit_number)) else '' end),s.shippingid, s.shippingname, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.facilitycode, d.firstexperimentid + GROUP BY CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number), r.labcontactid, se.beamlineoperator, TO_CHAR(se.startdate, 'HH24:MI DD-MM-YYYY'), (case when se.visit_number > 0 then (CONCAT(p.proposalcode, p.proposalnumber, '-', se.visit_number)) else '' end),s.shippingid, s.shippingname, d.code, d.barcode, d.storagelocation, d.dewarstatus, d.dewarid, d.trackingnumbertosynchrotron, d.trackingnumberfromsynchrotron, d.facilitycode, d.firstexperimentid ORDER BY $order", $args); - - if ($this->has_arg('did')) { - if (sizeof($dewars)) $this->_output($dewars[0]); - else $this->_error('No such dewar'); - } else $this->_output(array('total' => $tot, 'data' => $dewars)); - - } - - # ------------------------------------------------------------------------ - # Add a dewar to a shipment - function _add_dewar() { - global $dewar_weight; - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('SHIPPINGID')) $this->_error('No shipping id specified'); - if (!$this->has_arg('CODE')) $this->_error('No dewar name specified'); - - $ship = $this->db->pq("SELECT s.shippingid + + if ($this->has_arg('did')) { + if (sizeof($dewars)) + $this->_output($dewars[0]); + else + $this->_error('No such dewar'); + } else + $this->_output(array('total' => $tot, 'data' => $dewars)); + } + + # ------------------------------------------------------------------------ + # Add a dewar to a shipment + function _add_dewar() + { + global $dewar_weight; + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('SHIPPINGID')) + $this->_error('No shipping id specified'); + if (!$this->has_arg('CODE')) + $this->_error('No dewar name specified'); + + $ship = $this->db->pq("SELECT s.shippingid FROM shipping s - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('SHIPPINGID'))); - - if (!sizeof($ship)) $this->_error('No such shipment'); - - $to = $this->has_arg('TRACKINGNUMBERTOSYNCHROTRON') ? $this->arg('TRACKINGNUMBERTOSYNCHROTRON') : ''; - $from = $this->has_arg('TRACKINGNUMBERFROMSYNCHROTRON') ? $this->arg('TRACKINGNUMBERFROMSYNCHROTRON') : ''; - $fc = $this->has_arg('FACILITYCODE') ? $this->arg('FACILITYCODE') : ''; - $wg = $this->has_arg('WEIGHT') ? $this->arg('WEIGHT') : $dewar_weight; - $exp = null; - - if ($this->has_arg('FIRSTEXPERIMENTID')) { - $experimentId = $this->arg('FIRSTEXPERIMENTID'); - $exp = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; - } - - $this->db->pq("INSERT INTO dewar (dewarid,code,trackingnumbertosynchrotron,trackingnumberfromsynchrotron,shippingid,bltimestamp,dewarstatus,firstexperimentid,facilitycode,weight) - VALUES (s_dewar.nextval,:1,:2,:3,:4,CURRENT_TIMESTAMP,'opened',:5,:6,:7) RETURNING dewarid INTO :id", - array($this->arg('CODE'), $to, $from, $this->arg('SHIPPINGID'), $exp, $fc,$wg)); - - $id = $this->db->id(); - - # Need to generate barcode - $vis = ''; - if ($exp) { - $vr = $this->db->pq("SELECT s.beamlinename as bl,s.visit_number as vis FROM blsession s WHERE s.sessionid=:1", array($exp)); - if (sizeof($vr)) $vis = '-'.$vr[0]['VIS'].'-'.$vr[0]['BL']; - } - - $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($this->arg('prop').$vis.'-'.str_pad($id,7,'0',STR_PAD_LEFT), $id)); - - $this->_output(array('DEWARID' => $id)); - } - + WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('SHIPPINGID'))); - - - # Update shipment - function _update_shipment() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipping id specified'); - - $ship = $this->db->pq("SELECT s.shippingid FROM shipping s WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - - if (!sizeof($ship)) $this->_error('No such shipment'); - - $fields = array('SHIPPINGNAME', 'SAFETYLEVEL', 'COMMENTS', 'DELIVERYAGENT_AGENTNAME', 'DELIVERYAGENT_AGENTCODE', 'DELIVERYAGENT_SHIPPINGDATE', 'DELIVERYAGENT_DELIVERYDATE', 'SENDINGLABCONTACTID', 'RETURNLABCONTACTID', 'READYBYTIME', 'CLOSETIME', 'PHYSICALLOCATION'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $fl = ':1'; - if (in_array($f, array('DELIVERYAGENT_DELIVERYDATE', 'DELIVERYAGENT_SHIPPINGDATE'))) { - $fl = "TO_DATE(:1, 'DD-MM-YYYY')"; - } + if (!sizeof($ship)) + $this->_error('No such shipment'); + + $to = $this->has_arg('TRACKINGNUMBERTOSYNCHROTRON') ? $this->arg('TRACKINGNUMBERTOSYNCHROTRON') : ''; + $from = $this->has_arg('TRACKINGNUMBERFROMSYNCHROTRON') ? $this->arg('TRACKINGNUMBERFROMSYNCHROTRON') : ''; + $fc = $this->has_arg('FACILITYCODE') ? $this->arg('FACILITYCODE') : ''; + $wg = $this->has_arg('WEIGHT') ? $this->arg('WEIGHT') : $dewar_weight; + $exp = null; + + if ($this->has_arg('FIRSTEXPERIMENTID')) { + $experimentId = $this->arg('FIRSTEXPERIMENTID'); + $exp = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; + } + + $this->db->pq( + "INSERT INTO dewar (dewarid,code,trackingnumbertosynchrotron,trackingnumberfromsynchrotron,shippingid,bltimestamp,dewarstatus,firstexperimentid,facilitycode,weight) + VALUES (s_dewar.nextval,:1,:2,:3,:4,CURRENT_TIMESTAMP,'opened',:5,:6,:7) RETURNING dewarid INTO :id", + array($this->arg('CODE'), $to, $from, $this->arg('SHIPPINGID'), $exp, $fc, $wg) + ); + + $id = $this->db->id(); + + # Need to generate barcode + $vis = ''; + if ($exp) { + $vr = $this->db->pq("SELECT s.beamlinename as bl,s.visit_number as vis FROM blsession s WHERE s.sessionid=:1", array($exp)); + if (sizeof($vr)) + $vis = '-' . $vr[0]['VIS'] . '-' . $vr[0]['BL']; + } + + $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($this->arg('prop') . $vis . '-' . str_pad($id, 7, '0', STR_PAD_LEFT), $id)); + + $this->_output(array('DEWARID' => $id)); + } + + # Update shipment + function _update_shipment() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); - $this->db->pq("UPDATE shipping SET $f=$fl WHERE shippingid=:2", array($this->arg($f), $this->arg('sid'))); - $lcf = array('SENDINGLABCONTACTID' => 'LCOUT', 'RETURNLABCONTACTID' => 'LCRET'); - if (array_key_exists($f, $lcf)) { - $lc = $this->db->pq('SELECT cardname FROM labcontact WHERE labcontactid=:1', array($this->arg($f))); - if (sizeof($lc)) { - $this->_output(array($lcf[$f] => $lc[0]['CARDNAME'])); - } + $ship = $this->db->pq("SELECT s.shippingid FROM shipping s WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); - } else $this->_output(array($f => $this->arg($f))); + if (!sizeof($ship)) + $this->_error('No such shipment'); + $fields = array('SHIPPINGNAME', 'SAFETYLEVEL', 'COMMENTS', 'DELIVERYAGENT_AGENTNAME', 'DELIVERYAGENT_AGENTCODE', 'DELIVERYAGENT_FLIGHTCODE', 'DELIVERYAGENT_SHIPPINGDATE', 'DELIVERYAGENT_DELIVERYDATE', 'SENDINGLABCONTACTID', 'RETURNLABCONTACTID', 'READYBYTIME', 'CLOSETIME', 'PHYSICALLOCATION'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $fl = ':1'; + if (in_array($f, array('DELIVERYAGENT_DELIVERYDATE', 'DELIVERYAGENT_SHIPPINGDATE'))) { + $fl = "TO_DATE(:1, 'DD-MM-YYYY')"; } + + $this->db->pq("UPDATE shipping SET $f=$fl WHERE shippingid=:2", array($this->arg($f), $this->arg('sid'))); + + $lcf = array('SENDINGLABCONTACTID' => 'LCOUT', 'RETURNLABCONTACTID' => 'LCRET'); + if (array_key_exists($f, $lcf)) { + $lc = $this->db->pq('SELECT cardname FROM labcontact WHERE labcontactid=:1', array($this->arg($f))); + if (sizeof($lc)) { + $this->_output(array($lcf[$f] => $lc[0]['CARDNAME'])); + } + } else + $this->_output(array($f => $this->arg($f))); } + } + foreach ($this->extra_arg_list as $extra_arg_name) { + if ($this->has_arg($extra_arg_name)) { + $extra_arg_value = addslashes($this->arg($extra_arg_name)); + $shippingid = $this->arg('sid'); + $this->db->pq( + "UPDATE shipping SET extra = JSON_SET(IFNULL(extra, '{}'), :1, :2 ) WHERE shippingid=:3", + array('$.' . $extra_arg_name, $extra_arg_value, $shippingid) + ); + $this->_output(array($extra_arg_name => $extra_arg_value)); + } } - - - # Update dewar in shipment - function _update_dewar() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('did')) $this->_error('No dewar id specified'); + } + - $dewar = $this->db->pq("SELECT d.dewarid,d.shippingid FROM dewar d + # Update dewar in shipment + function _update_dewar() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('did')) + $this->_error('No dewar id specified'); + + $dewar = $this->db->pq("SELECT d.dewarid,d.shippingid FROM dewar d INNER JOIN shipping s ON d.shippingid = s.shippingid - WHERE s.proposalid = :1 AND d.dewarid = :2", array($this->proposalid,$this->arg('did'))); + WHERE s.proposalid = :1 AND d.dewarid = :2", array($this->proposalid, $this->arg('did'))); - if (!sizeof($dewar)) $this->_error('No such dewar'); + if (!sizeof($dewar)) + $this->_error('No such dewar'); - if ($this->has_arg('FIRSTEXPERIMENTID')) { - $experimentId = $this->arg('FIRSTEXPERIMENTID'); - $sessionId = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; - $chk = $this->db->pq("SELECT 1 FROM shippinghassession WHERE shippingid=:1 AND sessionid=:2", array($dewar[0]['SHIPPINGID'], $this->arg('FIRSTEXPERIMENTID'))); + if ($this->has_arg('FIRSTEXPERIMENTID')) { + $experimentId = $this->arg('FIRSTEXPERIMENTID'); + $sessionId = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; + $chk = $this->db->pq("SELECT 1 FROM shippinghassession WHERE shippingid=:1 AND sessionid=:2", array($dewar[0]['SHIPPINGID'], $this->arg('FIRSTEXPERIMENTID'))); - if (!sizeof($chk) && !is_null($sessionId)) { - $this->db->pq("INSERT INTO shippinghassession (shippingid, sessionid) VALUES (:1,:2)", array($dewar[0]['SHIPPINGID'], $this->arg('FIRSTEXPERIMENTID'))); - } + if (!sizeof($chk) && !is_null($sessionId)) { + $this->db->pq("INSERT INTO shippinghassession (shippingid, sessionid) VALUES (:1,:2)", array($dewar[0]['SHIPPINGID'], $this->arg('FIRSTEXPERIMENTID'))); } + } - $fields = array('CODE', 'TRACKINGNUMBERTOSYNCHROTRON', 'TRACKINGNUMBERFROMSYNCHROTRON', 'FIRSTEXPERIMENTID', 'FACILITYCODE', 'WEIGHT'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - if ($f === 'FIRSTEXPERIMENTID') { - $experimentId = $this->arg('FIRSTEXPERIMENTID'); - $sessionId = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; - $this->db->pq("UPDATE dewar SET $f=:1 WHERE dewarid=:2", array($sessionId, $this->arg('did'))); + $fields = array('CODE', 'TRACKINGNUMBERTOSYNCHROTRON', 'TRACKINGNUMBERFROMSYNCHROTRON', 'FIRSTEXPERIMENTID', 'FACILITYCODE', 'WEIGHT'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + if ($f === 'FIRSTEXPERIMENTID') { + $experimentId = $this->arg('FIRSTEXPERIMENTID'); + $sessionId = !empty($experimentId) ? $this->arg('FIRSTEXPERIMENTID') : NULL; + $this->db->pq("UPDATE dewar SET $f=:1 WHERE dewarid=:2", array($sessionId, $this->arg('did'))); - $visit = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit + $visit = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE s.sessionid=:1", array($sessionId)); - if (sizeof($visit)) { - $this->_output(array($f => $sessionId, 'EXP' => $visit[0]['VISIT'])); - } else { - $this->_output(1); - } + if (sizeof($visit)) { + $this->_output(array($f => $sessionId, 'EXP' => $visit[0]['VISIT'])); } else { - $this->db->pq("UPDATE dewar SET $f=:1 WHERE dewarid=:2", array($this->arg($f), $this->arg('did'))); - $this->_output(array($f => $this->arg($f))); + $this->_output(1); } - + } else { + $this->db->pq("UPDATE dewar SET $f=:1 WHERE dewarid=:2", array($this->arg($f), $this->arg('did'))); + $this->_output(array($f => $this->arg($f))); } } } - /* - * Added for Logistics App. Stores basic info about state of the dewar via comments. - * No user is logged in hence this is provided as extra end point. - * Once authentication/authorization is moved into separate service merge this with other patch request. - */ - function _update_dewar_comments() { - if (!$this->has_arg('did')) $this->_error('No dewar specified'); - if (!$this->has_arg('COMMENTS')) $this->_error('No dewar comments specified'); - - $dewars = $this->db->pq("SELECT d.dewarid + } + /* + * Added for Logistics App. Stores basic info about state of the dewar via comments. + * No user is logged in hence this is provided as extra end point. + * Once authentication/authorization is moved into separate service merge this with other patch request. + */ + function _update_dewar_comments() + { + if (!$this->has_arg('did')) + $this->_error('No dewar specified'); + if (!$this->has_arg('COMMENTS')) + $this->_error('No dewar comments specified'); + + $dewars = $this->db->pq("SELECT d.dewarid FROM dewar d WHERE d.dewarid = :1", array($this->arg('did'))); - if (!sizeof($dewars)) $this->_error('Dewar ' . $this->arg('did') . ' not found', 404); + if (!sizeof($dewars)) + $this->_error('Dewar ' . $this->arg('did') . ' not found', 404); + + $dewar = $dewars[0]; - $dewar = $dewars[0]; + $this->db->pq("UPDATE dewar set comments=:2 WHERE dewarid=:1", array($dewar['DEWARID'], $this->arg('COMMENTS'))); + + $this->_output(array('DEWARID' => $dewar['DEWARID'])); + } - $this->db->pq("UPDATE dewar set comments=:2 WHERE dewarid=:1", array($dewar['DEWARID'], $this->arg('COMMENTS'))); - $this->_output(array('DEWARID' => $dewar['DEWARID'])); + # Update shipping status to sent, email for CL3 + function _send_shipment() + { + global $cl3_email; + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); + + $ship = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, s.safetylevel, s.shippingid, s.deliveryagent_agentname, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as shippingdate, TO_CHAR(s.deliveryagent_deliverydate, 'DD-MM-YYYY') as deliverydate, s.shippingname, s.comments, c.cardname as lcout, + SUM(IF(d.dewarstatus = 'processing', 1, 0)) as processing + FROM shipping s INNER JOIN proposal p ON s.proposalid = p.proposalid + LEFT OUTER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid + LEFT OUTER JOIN dewar d ON d.shippingid = s.shippingid + WHERE p.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); + + if (!sizeof($ship)) + $this->_error('No such shipment'); + $ship = $ship[0]; + + if ($ship['PROCESSING']) { + $this->_error('Cant set shipment status, one or more dewars is being processed'); } - - # Update shipping status to sent, email for CL3 - function _send_shipment() { - global $cl3_email; - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipping id specified'); - - $ship = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, s.safetylevel, s.shippingid, s.deliveryagent_agentname, TO_CHAR(s.deliveryagent_shippingdate, 'DD-MM-YYYY') as shippingdate, TO_CHAR(s.deliveryagent_deliverydate, 'DD-MM-YYYY') as deliverydate, s.shippingname, s.comments, c.cardname as lcout FROM shipping s INNER JOIN proposal p ON s.proposalid = p.proposalid LEFT OUTER JOIN labcontact c ON s.sendinglabcontactid = c.labcontactid WHERE p.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - - if (!sizeof($ship)) $this->_error('No such shipment'); - $ship = $ship[0]; - - $this->db->pq("UPDATE shipping SET shippingstatus='sent to facility' where shippingid=:1", array($ship['SHIPPINGID'])); - $this->db->pq("UPDATE dewar SET dewarstatus='sent to facility' where shippingid=:1", array($ship['SHIPPINGID'])); - - $dewars = $this->db->pq("SELECT d.dewarid, s.visit_number as vn, s.beamlinename as bl, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as startdate + $this->db->pq("UPDATE shipping SET shippingstatus='sent to facility' where shippingid=:1", array($ship['SHIPPINGID'])); + $this->db->pq("UPDATE dewar SET dewarstatus='sent to facility', storagelocation='off-site' where shippingid=:1", array($ship['SHIPPINGID'])); + + $dewars = $this->db->pq("SELECT d.dewarid, s.visit_number as vn, s.beamlinename as bl, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as startdate FROM dewar d LEFT OUTER JOIN blsession s ON s.sessionid = d.firstexperimentid WHERE d.shippingid=:1", array($ship['SHIPPINGID'])); + foreach ($dewars as $d) { + $this->db->pq( + "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) + VALUES (s_dewartransporthistory.nextval,:1,'sent to facility','off-site',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", + array($d['DEWARID']) + ); + } + + # Send email if CL3 + if ($ship['SAFETYLEVEL'] == 'Red') { + $exps = array(); foreach ($dewars as $d) { - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,arrivaldate) - VALUES (s_dewartransporthistory.nextval,:1,'sent to facility',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", - array($d['DEWARID'])); + array_push($exps, $ship['PROP'] . '-' . $d['VN'] . ' on ' . $d['BL'] . ' starting ' . $d['STARTDATE']); } + $ship['EXPS'] = $exps; - # Send email if CL3 - if ($ship['SAFETYLEVEL'] == 'Red') { - $exps = array(); - foreach ($dewars as $d) { - array_push($exps, $ship['PROP'].'-'.$d['VN'].' on '.$d['BL'].' starting '.$d['STARTDATE']); - } - $ship['EXPS'] = $exps; - - $email = new Email('dewar-redexp', '*** RED safety level shipment sent for '.$ship['PROP'].' ***'); - $email->data = $ship; - $email->send($cl3_email); - } - - $this->_output(1); - + $email = new Email('dewar-redexp', '*** RED safety level shipment sent for ' . $ship['PROP'] . ' ***'); + $email->data = $ship; + $email->send($cl3_email); } - - - # Show and accept terms to use diamonds shipping account - function _get_terms() { - global $dhl_terms; - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipment specified'); + $this->_output(1); + } + + + # Show and accept terms to use diamonds shipping account + function _get_terms() + { + global $dhl_terms; + + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipment specified'); - $terms = $this->db->pq("SELECT p.givenname, p.familyname, TO_CHAR(cta.timestamp, 'HH24:MI DD-MM-YYYY') as timestamp, 1 as accepted + $terms = $this->db->pq("SELECT p.givenname, p.familyname, TO_CHAR(cta.timestamp, 'HH24:MI DD-MM-YYYY') as timestamp, 1 as accepted FROM couriertermsaccepted cta INNER JOIN person p ON p.personid = cta.personid WHERE cta.proposalid=:1 AND cta.shippingid=:2", array($this->proposalid, $this->arg('sid'))); - $terms_list = file_exists($dhl_terms) ? file_get_contents($dhl_terms) : ''; - if (sizeof($terms)) { - $terms[0]['TERMS'] = $terms_list; - $this->_output($terms[0]); - } else { - $this->_output(array('TERMS' => $terms_list)); - } - } - - function _accept_terms() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipment specified'); - - $this->db->pq("INSERT INTO couriertermsaccepted (couriertermsacceptedid,proposalid,personid,shippingid,timestamp) - VALUES (s_dhltermsaccepted.nextval, :1, :2, :3, CURRENT_TIMESTAMP)", array($this->proposalid, $this->user->personid, $this->arg('sid'))); - - $this->_output(array('ACCEPTED' => 1)); + $terms_list = file_exists($dhl_terms) ? file_get_contents($dhl_terms) : ''; + if (sizeof($terms)) { + $terms[0]['TERMS'] = $terms_list; + $this->_output($terms[0]); + } else { + $this->_output(array('TERMS' => $terms_list)); } + } - - # Check if a barcode exists - function _check_container() { - $cont = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, c.barcode + function _accept_terms() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipment specified'); + + $this->db->pq("INSERT INTO couriertermsaccepted (couriertermsacceptedid,proposalid,personid,shippingid,timestamp) + VALUES (s_dhltermsaccepted.nextval, :1, :2, :3, CURRENT_TIMESTAMP)", array($this->proposalid, $this->user->personId, $this->arg('sid'))); + + $this->_output(array('ACCEPTED' => 1)); + } + + + # Check if a barcode exists + function _check_container() + { + $cont = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, c.barcode FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE c.barcode=:1", array($this->arg('BARCODE'))); - if (!sizeof($cont)) $this->_error('Barcode not used'); - $this->_output($cont[0]); - } + if (!sizeof($cont)) + $this->_error('Barcode not used'); + $this->_output($cont[0]); + } + function _get_all_containers() + { + //$this->db->set_debug(True); + if (!$this->has_arg('prop') && !$this->has_arg('visit') && !$this->staff) + $this->_error('No proposal specified'); + + $having = ''; + $subsamplesInTotal = ""; + $totalQuery = new DatabaseQueryBuilder($this->db); + + if ($this->has_arg('visit')) { + $join = " INNER JOIN blsession ses2 ON ses2.proposalid = p.proposalid"; + $args = array($this->arg('visit')); + $where = "CONCAT(p.proposalcode, p.proposalnumber, '-', ses2.visit_number) LIKE :1"; + $totalQuery->joinClause("INNER JOIN blsession ses2 ON ses2.proposalid = p.proposalid"); + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + $totalQuery->joinClause("INNER JOIN shipping sh ON sh.shippingid = d.shippingid"); + $totalQuery->joinClause("INNER JOIN proposal p ON p.proposalid = sh.proposalid"); + } else if ($this->has_arg('all') && $this->staff) { + $join = ''; + $args = array(); + $where = '1=1'; + } else { + $join = ''; + $args = array($this->proposalid); + $where = 'sh.proposalid=:1'; + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + $totalQuery->joinClause("INNER JOIN shipping sh ON sh.shippingid = d.shippingid"); + } - function _get_all_containers() { - //$this->db->set_debug(True); - if (!$this->has_arg('prop') && !$this->has_arg('visit') && !$this->staff) $this->_error('No proposal specified'); - - $having = ''; - - if ($this->has_arg('visit')) { - $join = " INNER JOIN blsession ses2 ON ses2.proposalid = p.proposalid"; - $args = array($this->arg('visit')); - $where = "CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), ses2.visit_number) LIKE :1"; + if ($this->has_arg('ty')) { + if ($this->arg('ty') == 'plate') { + $where .= " AND c.containertype NOT LIKE '%puck'"; + } else if ($this->arg('ty') == 'puck') { + $where .= " AND c.containertype LIKE '%puck'"; + } else if ($this->arg('ty') == 'imager') { + $where .= " AND c.imagerid IS NOT NULL"; + } else if ($this->arg('ty') == 'todispose') { + $where .= " AND c.imagerid IS NOT NULL"; + $having .= " HAVING (TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24) > 42"; + $totalQuery->joinClause("LEFT OUTER JOIN containerinspection ci ON ci.containerid = c.containerid AND ci.state = 'Completed'"); + } else if ($this->arg('ty') == 'queued') { + $where .= " AND cq.containerqueueid IS NOT NULL"; + $totalQuery->joinClause("LEFT OUTER JOIN containerqueue cq ON cq.containerid = c.containerid AND cq.completedtimestamp IS NULL"); + } else if ($this->arg('ty') == 'completed') { + $where .= " AND cq2.completedtimestamp IS NOT NULL"; + $totalQuery->joinClause("LEFT OUTER JOIN containerqueue cq2 ON cq2.containerid = c.containerid AND cq2.completedtimestamp IS NOT NULL"); + $this->args['sort_by'] = 'COMPLETEDTIMESTAMP'; + $this->args['order'] = 'desc'; + } else if ($this->arg('ty') == 'processing') { + $where .= " AND c.containerstatus = 'processing'"; + } else if ($this->arg('ty') == 'subsamples') { + $having .= " HAVING subsamples > 0"; + $subsamplesInTotal = ", count(distinct ss.blsubsampleid) as subsamples"; + $totalQuery->joinClause("LEFT OUTER JOIN blsample s ON s.containerid = c.containerid"); + $totalQuery->joinClause("LEFT OUTER JOIN blsubsample ss ON s.blsampleid = ss.blsampleid AND ss.source='manual'"); + } + } - } else if ($this->has_arg('all') && $this->staff) { - $join = ''; - $args = array(); - $where = '1=1'; - } else { - $join = ''; - $args = array($this->proposalid); - $where = 'sh.proposalid=:1'; - } + if ($this->has_arg('PUCK')) { + $where .= " AND c.containertype LIKE '%puck'"; + } - if ($this->has_arg('ty')) { - if ($this->arg('ty') == 'plate') { - $where .= " AND c.containertype NOT LIKE '%puck'"; - } else if ($this->arg('ty') == 'puck') { - $where .= " AND c.containertype LIKE '%puck'"; - } else if ($this->arg('ty') == 'imager') { - $where .= " AND c.imagerid IS NOT NULL"; - } else if ($this->arg('ty') == 'todispose') { - $where .= " AND c.imagerid IS NOT NULL"; - $having .= " HAVING (TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24) > 42"; - } else if ($this->arg('ty') == 'queued') { - $where .= " AND cq.containerqueueid IS NOT NULL"; - } else if ($this->arg('ty') == 'completed') { - $where .= " AND cq2.completedtimestamp IS NOT NULL"; - $this->args['sort_by'] = 'COMPLETEDTIMESTAMP'; - $this->args['order'] = 'desc'; - } else if ($this->arg('ty') == 'processing') { - $where .= " AND c.containerstatus = 'processing'"; - } else if ($this->arg('ty') == 'subsamples') { - $having .= " HAVING COUNT(distinct ss.blsubsampleid) > 0"; - } - } + if ($this->has_arg('did')) { + $where .= ' AND d.dewarid=:' . (sizeof($args) + 1); + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + array_push($args, $this->arg('did')); + } + if ($this->has_arg('sid')) { + $where .= ' AND sh.shippingid=:' . (sizeof($args) + 1); + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + $totalQuery->joinClause("INNER JOIN shipping sh ON sh.shippingid = d.shippingid"); + array_push($args, $this->arg('sid')); + } + if ($this->has_arg('cid')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('cid')); + } + if ($this->has_arg('pid')) { + // $this->db->set_debug(True); + $join .= ' LEFT OUTER JOIN crystal cr ON cr.crystalid = s.crystalid LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid'; + $where .= ' AND pr.proteinid=:' . (sizeof($args) + 1); + $totalQuery->joinClause("LEFT OUTER JOIN blsample s ON s.containerid = c.containerid"); + $totalQuery->joinClause("LEFT OUTER JOIN crystal cr ON cr.crystalid = s.crystalid"); + $totalQuery->joinClause("LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid"); + array_push($args, $this->arg('pid')); + } - if ($this->has_arg('PUCK')) { - $where .= " AND c.containertype LIKE '%puck'"; - } + if ($this->has_arg('assigned')) { + $where .= " AND d.dewarstatus LIKE 'processing' AND c.samplechangerlocation > 0"; + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + } - - if ($this->has_arg('did')) { - $where .= ' AND d.dewarid=:'.(sizeof($args)+1); - array_push($args, $this->arg('did')); - } - if ($this->has_arg('cid')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('cid')); - } + if ($this->has_arg('bl')) { + $where .= " AND c.beamlinelocation LIKE :" . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } - if ($this->has_arg('pid')) { - // $this->db->set_debug(True); - $where .= ' AND pr.proteinid=:'.(sizeof($args)+1); - array_push($args, $this->arg('pid')); - } - - if ($this->has_arg('assigned')) { - $where .= " AND d.dewarstatus LIKE 'processing' AND c.samplechangerlocation > 0"; - } - - if ($this->has_arg('bl')) { - $where .= " AND c.beamlinelocation LIKE :".(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } + if ($this->has_arg('unassigned')) { + $where .= " AND c.containerid NOT IN (SELECT c.containerid FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid WHERE d.dewarstatus LIKE 'processing' AND c.samplechangerlocation > 0 AND c.beamlinelocation=:" . (sizeof($args) + 1) . ")"; - if ($this->has_arg('unassigned')) { - $where .= " AND c.containerid NOT IN (SELECT c.containerid FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid WHERE d.dewarstatus LIKE 'processing' AND c.samplechangerlocation > 0 AND c.beamlinelocation=:".(sizeof($args)+1).")"; - - array_push($args, $this->arg('unassigned')); - $this->args['sort_by'] = 'SHIPPINGID'; - $this->args['order'] = 'desc'; - } + array_push($args, $this->arg('unassigned')); + $totalQuery->joinClause("INNER JOIN dewar d ON d.dewarid = c.dewarid"); + $totalQuery->joinClause("INNER JOIN shipping sh ON sh.shippingid = d.shippingid"); + $this->args['sort_by'] = 'SHIPPINGID'; + $this->args['order'] = 'desc'; + } - if ($this->has_arg('imager')) { - if ($this->arg('imager') == '1') $where .= ' AND c.imagerid IS NOT NULL'; - else $where .= ' AND c.imagerid IS NULL AND c.requestedimagerid IS NULL'; - } + if ($this->has_arg('imager')) { + if ($this->arg('imager') == '1') + $where .= ' AND c.imagerid IS NOT NULL'; + else + $where .= ' AND c.imagerid IS NULL AND c.requestedimagerid IS NULL'; + } - if ($this->has_arg('iid')) { - $where .= ' AND c.imagerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('iid')); - } + if ($this->has_arg('iid')) { + $where .= ' AND c.imagerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('iid')); + } - if ($this->has_arg('CONTAINERREGISTRYID')) { - $where .= ' AND c.containerregistryid = :'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERREGISTRYID')); - } + if ($this->has_arg('CONTAINERREGISTRYID')) { + $where .= ' AND c.containerregistryid = :' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERREGISTRYID')); + } - if ($this->has_arg('currentuser')) { - $where .= ' AND c.ownerid = :'.(sizeof($args)+1); - array_push($args, $this->user->personid); - } + if ($this->has_arg('currentuser')) { + $where .= ' AND c.ownerid = :' . (sizeof($args) + 1); + array_push($args, $this->user->personId); + } - $tot = $this->db->pq("SELECT count(distinct c.containerid) as tot + $tot = $this->db->pq("SELECT count(distinct c.containerid) as tot + $subsamplesInTotal FROM container c - INNER JOIN dewar d ON d.dewarid = c.dewarid - INNER JOIN shipping sh ON sh.shippingid = d.shippingid - INNER JOIN proposal p ON p.proposalid = sh.proposalid - LEFT OUTER JOIN blsample s ON s.containerid = c.containerid - LEFT OUTER JOIN blsubsample ss ON s.blsampleid = ss.blsampleid AND ss.source='manual' - LEFT OUTER JOIN crystal cr ON cr.crystalid = s.crystalid - LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid - LEFT OUTER JOIN containerinspection ci ON ci.containerid = c.containerid AND ci.state = 'Completed' - LEFT OUTER JOIN containerqueue cq ON cq.containerid = c.containerid AND cq.completedtimestamp IS NULL - LEFT OUTER JOIN containerqueue cq2 ON cq2.containerid = c.containerid AND cq2.completedtimestamp IS NOT NULL - $join + {$totalQuery->getJoins()} WHERE $where $having", $args); - $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; - - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(c.code) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) OR lower(c.barcode) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } - - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'c.bltimestamp DESC'; - - if ($this->has_arg('ty')) { - if ($this->arg('ty') == 'todispose') { - $order = 'c.requestedreturn DESC, age DESC'; - } - - if ($this->arg('ty') == 'queued') { - $order = 'cq.containerqueueid ASC'; - } + $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; + + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(c.code) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(c.barcode) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')))"; + array_push($args, $this->arg('s')); + array_push($args, $this->arg('s')); + } + + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'c.bltimestamp DESC'; + + if ($this->has_arg('ty')) { + if ($this->arg('ty') == 'todispose') { + $order = 'c.requestedreturn DESC, age DESC'; } - - if ($this->has_arg('sort_by')) { - $cols = array('NAME' => 'c.code', 'DEWAR' => 'd.code', 'SHIPMENT' => 'sh.shippingname', 'SAMPLES' => 'count(s.blsampleid)', 'SHIPPINGID' =>'sh.shippingid', 'LASTINSPECTION' => 'max(ci.bltimestamp)', 'INSPECTIONS' => 'count(ci.containerinspectionid)', - 'DCCOUNT' => 'COUNT(distinct dc.datacollectionid)', 'SUBSAMPLES' => 'count(distinct ss.blsubsampleid)', - 'LASTQUEUECOMPLETED' => 'max(cq2.completedtimestamp)', 'QUEUEDTIMESTAMP' => 'max(cq.createdtimestamp)' - ); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; + + if ($this->arg('ty') == 'queued') { + $order = 'cq.containerqueueid ASC'; } - // $this->db->set_debug(True); - $rows = $this->db->paginate("SELECT round(TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as age, case when count(ci2.containerinspectionid) > 1 then 0 else 1 end as allow_adhoc, sch.name as schedule, c.scheduleid, c.screenid, sc.name as screen, c.imagerid, i.temperature as temperature, i.name as imager, TO_CHAR(max(ci.bltimestamp), 'HH24:MI DD-MM-YYYY') as lastinspection, round(TIMESTAMPDIFF('HOUR', max(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as lastinspectiondays, count(distinct ci.containerinspectionid) as inspections, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.bltimestamp, c.samplechangerlocation, c.beamlinelocation, d.dewarstatus, c.containertype, c.capacity, c.containerstatus, c.containerid, c.code as name, d.code as dewar, sh.shippingname as shipment, d.dewarid, sh.shippingid, count(distinct s.blsampleid) as samples, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), ses.visit_number) as visit, ses.beamlinename, c.requestedreturn, c.requestedimagerid, i2.name as requestedimager, c.comments, c.experimenttype, c.storagetemperature, c.barcode, reg.barcode as registry, reg.containerregistryid, - count(distinct ss.blsubsampleid) as subsamples, - ses3.beamlinename as firstexperimentbeamline, - pp.name as pipeline, + } + + if ($this->has_arg('sort_by')) { + $cols = array( + 'NAME' => 'c.code', 'DEWAR' => 'd.code', 'SHIPMENT' => 'sh.shippingname', 'SAMPLES' => 'count(s.blsampleid)', 'SHIPPINGID' => 'sh.shippingid', 'LASTINSPECTION' => 'max(ci.bltimestamp)', 'INSPECTIONS' => 'count(ci.containerinspectionid)', + 'DCCOUNT' => 'COUNT(distinct dc.datacollectionid)', 'SUBSAMPLES' => 'subsamples', + 'LASTQUEUECOMPLETED' => 'max(cq2.completedtimestamp)', 'QUEUEDTIMESTAMP' => 'max(cq.createdtimestamp)' + ); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } + // $this->db->set_debug(True); + $rows = $this->db->paginate("SELECT round(TIMESTAMPDIFF('HOUR', min(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as age, case when count(ci2.containerinspectionid) > 1 then 0 else 1 end as allow_adhoc, c.scheduleid, c.screenid, sc.name as screen, c.imagerid, i.temperature as temperature, i.name as imager, TO_CHAR(max(ci.bltimestamp), 'HH24:MI DD-MM-YYYY') as lastinspection, round(TIMESTAMPDIFF('HOUR', max(ci.bltimestamp), CURRENT_TIMESTAMP)/24,1) as lastinspectiondays, count(distinct ci.containerinspectionid) as inspections, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.bltimestamp, c.samplechangerlocation, c.beamlinelocation, d.dewarstatus, c.containertype, c.capacity, c.containerstatus, c.containerid, c.code as name, d.code as dewar, sh.shippingname as shipment, d.dewarid, sh.shippingid, count(distinct s.blsampleid) as samples, cq.containerqueueid, TO_CHAR(cq.createdtimestamp, 'DD-MM-YYYY HH24:MI') as queuedtimestamp, CONCAT(p.proposalcode, p.proposalnumber, '-', ses.visit_number) as visit, ses.beamlinename, c.requestedreturn, c.requestedimagerid, c.comments, c.experimenttype, c.storagetemperature, c.barcode, reg.barcode as registry, reg.containerregistryid, + (SELECT sch.name FROM schedule sch WHERE sch.scheduleid = c.scheduleid) as schedule, + (SELECT i2.name FROM imager i2 WHERE i2.imagerid = c.requestedimagerid) as requestedimager, + (SELECT count(distinct ss.blsubsampleid) FROM blsubsample ss RIGHT OUTER JOIN blsample s2 ON s2.blsampleid = ss.blsampleid WHERE s2.containerid = c.containerid AND ss.source='manual') as subsamples, + (SELECT ses3.beamlinename FROM blsession ses3 WHERE d.firstexperimentid = ses3.sessionid) as firstexperimentbeamline, + (SELECT pp.name FROM processingpipeline pp WHERE c.prioritypipelineid = pp.processingpipelineid) as pipeline, TO_CHAR(max(cq2.completedtimestamp), 'HH24:MI DD-MM-YYYY') as lastqueuecompleted, TIMESTAMPDIFF('MINUTE', max(cq2.completedtimestamp), max(cq2.createdtimestamp)) as lastqueuedwell, - c.ownerid, CONCAT(pe.givenname, ' ', pe.familyname) as owner + c.ownerid, + (SELECT CONCAT(pe.givenname, ' ', pe.familyname) FROM person pe WHERE c.ownerid = pe.personid) as owner FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid - LEFT OUTER JOIN blsession ses3 ON d.firstexperimentid = ses3.sessionid INNER JOIN shipping sh ON sh.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = sh.proposalid LEFT OUTER JOIN blsample s ON s.containerid = c.containerid - LEFT OUTER JOIN blsubsample ss ON s.blsampleid = ss.blsampleid AND ss.source='manual' - LEFT OUTER JOIN crystal cr ON cr.crystalid = s.crystalid - LEFT OUTER JOIN protein pr ON pr.proteinid = cr.proteinid LEFT OUTER JOIN containerinspection ci ON ci.containerid = c.containerid AND ci.state = 'Completed' LEFT OUTER JOIN imager i ON i.imagerid = c.imagerid - LEFT OUTER JOIN imager i2 ON i2.imagerid = c.requestedimagerid LEFT OUTER JOIN screen sc ON sc.screenid = c.screenid - LEFT OUTER JOIN schedule sch ON sch.scheduleid = c.scheduleid LEFT OUTER JOIN containerinspection ci2 ON ci2.containerid = c.containerid AND ci2.state != 'Completed' AND ci2.manual!=1 AND ci2.schedulecomponentid IS NULL LEFT OUTER JOIN containerqueue cq ON cq.containerid = c.containerid AND cq.completedtimestamp IS NULL LEFT OUTER JOIN containerqueue cq2 ON cq2.containerid = c.containerid AND cq2.completedtimestamp IS NOT NULL LEFT OUTER JOIN containerregistry reg ON reg.containerregistryid = c.containerregistryid - LEFT OUTER JOIN blsession ses ON c.sessionid = ses.sessionid - LEFT OUTER JOIN processingpipeline pp ON c.prioritypipelineid = pp.processingpipelineid - LEFT OUTER JOIN person pe ON c.ownerid = pe.personid $join WHERE $where @@ -1497,64 +1860,70 @@ function _get_all_containers() { $having ORDER BY $order", $args); - if ($this->has_arg('cid')) { - if (sizeof($rows))$this->_output($rows[0]); - else $this->_error('No such container'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); - - } - - + if ($this->has_arg('cid')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such container'); + } else + $this->_output(array( + 'total' => $tot, + 'data' => $rows, + )); + } + - function _queue_container() { - if (!$this->has_arg('CONTAINERID')) $this->_error('No container specified'); - $chkc = $this->db->pq("SELECT c.containerid,c.containerstatus,c.containertype FROM container c + function _queue_container() + { + if (!$this->has_arg('CONTAINERID')) + $this->_error('No container specified'); + + $chkc = $this->db->pq("SELECT c.containerid,c.containerstatus,c.containertype FROM container c INNER JOIN dewar d ON c.dewarid = d.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE c.containerid=:1 AND p.proposalid=:2", array($this->arg('CONTAINERID'), $this->proposalid)); - - if (!sizeof($chkc)) $this->_error('No such container'); + + if (!sizeof($chkc)) + $this->_error('No such container'); - if ($this->has_arg('UNQUEUE')) { - $chkq = $this->db->pq("SELECT containerqueueid FROM containerqueue WHERE containerid=:1 AND completedtimestamp IS NULL", array($this->arg('CONTAINERID'))); - if (!sizeof($chkq)) $this->_error('That container is not queued'); + if ($this->has_arg('UNQUEUE')) { + $chkq = $this->db->pq("SELECT containerqueueid FROM containerqueue WHERE containerid=:1 AND completedtimestamp IS NULL", array($this->arg('CONTAINERID'))); + if (!sizeof($chkq)) + $this->_error('That container is not queued'); - $validRequest = false; + $validRequest = false; - if (stripos($chkc[0]['CONTAINERTYPE'], 'puck') !== false) { - // Then we are a puck type and we allow pucks to be unqueued at any time + if (stripos($chkc[0]['CONTAINERTYPE'], 'puck') !== false) { + // Then we are a puck type and we allow pucks to be unqueued at any time + $validRequest = true; + } else { + if (in_array($chkc[0]['CONTAINERSTATUS'], array('in_storage', 'disposed', null))) { $validRequest = true; - } else { - if (in_array($chkc[0]['CONTAINERSTATUS'], array('in_storage', 'disposed', null))) { - $validRequest = true; - } - } - if (!$validRequest) { - $this->_error('Container is awaiting data collection and cannot be unqueued'); } + } + if (!$validRequest) { + $this->_error('Container is awaiting data collection and cannot be unqueued'); + } - $cqid = $chkq[0]['CONTAINERQUEUEID']; - - $this->db->pq("UPDATE containerqueuesample SET containerqueueid = NULL WHERE containerqueueid=:1", array($cqid)); - $this->db->pq("DELETE FROM containerqueue WHERE containerqueueid=:1", array($cqid)); - $this->_output(); + $cqid = $chkq[0]['CONTAINERQUEUEID']; + $this->db->pq("UPDATE containerqueuesample SET containerqueueid = NULL WHERE containerqueueid=:1", array($cqid)); + $this->db->pq("DELETE FROM containerqueue WHERE containerqueueid=:1", array($cqid)); + $this->_output(); + } else { + $chkq = $this->db->pq("SELECT containerid, containerqueueid FROM containerqueue WHERE containerid=:1 AND completedtimestamp IS NULL", array($this->arg('CONTAINERID'))); + if (!sizeof($chkq)) { + $this->db->pq("INSERT INTO containerqueue (containerid, personid) VALUES (:1, :2)", array($this->arg('CONTAINERID'), $this->user->personId)); + $qid = $this->db->id(); } else { - $chkq = $this->db->pq("SELECT containerid, containerqueueid FROM containerqueue WHERE containerid=:1 AND completedtimestamp IS NULL", array($this->arg('CONTAINERID'))); - if (!sizeof($chkq)) { - $this->db->pq("INSERT INTO containerqueue (containerid, personid) VALUES (:1, :2)", array($this->arg('CONTAINERID'), $this->user->personid)); - $qid = $this->db->id(); - } else { - $qid = $chkq[0]['CONTAINERQUEUEID']; - } + $qid = $chkq[0]['CONTAINERQUEUEID']; + } - $samples = $this->db->pq("SELECT ss.blsubsampleid, cqs.containerqueuesampleid FROM blsubsample ss + $samples = $this->db->pq("SELECT ss.blsubsampleid, cqs.containerqueuesampleid FROM blsubsample ss INNER JOIN blsample s ON s.blsampleid = ss.blsampleid INNER JOIN container c ON c.containerid = s.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -1563,148 +1932,161 @@ function _queue_container() { INNER JOIN containerqueuesample cqs ON cqs.blsubsampleid = ss.blsubsampleid WHERE p.proposalid=:1 AND c.containerid=:2 AND cqs.containerqueueid IS NULL", array($this->proposalid, $this->arg('CONTAINERID'))); - foreach ($samples as $s) { - $this->db->pq("UPDATE containerqueuesample SET containerqueueid=:1 WHERE containerqueuesampleid=:2", array($qid, $s['CONTAINERQUEUESAMPLEID'])); - } - - $this->_output(array('CONTAINERQUEUEID' => $qid)); + foreach ($samples as $s) { + $this->db->pq("UPDATE containerqueuesample SET containerqueueid=:1 WHERE containerqueuesampleid=:2", array($qid, $s['CONTAINERQUEUESAMPLEID'])); } + + $this->_output(array('CONTAINERQUEUEID' => $qid)); } + } - - # Move Container - function _move_container() { - if (!$this->has_arg('cid')) $this->_error('No container specified'); - if (!$this->has_arg('did')) $this->_error('No dewar specified'); - - $chkd = $this->db->pq("SELECT d.dewarid FROM dewar d INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE d.dewarid=:1 AND p.proposalid=:2", array($this->arg('did'), $this->proposalid)); - $chkc = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON c.dewarid = d.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE c.containerid=:1 AND p.proposalid=:2", array($this->arg('cid'), $this->proposalid)); - - if (sizeof($chkd) && sizeof($chkc)) { - $this->db->pq("UPDATE container SET dewarid=:1 WHERE containerid=:2", array($this->arg('did'), $this->arg('cid'))); - $this->_output(1); - } - + + # Move Container + function _move_container() + { + if (!$this->has_arg('cid')) + $this->_error('No container specified'); + if (!$this->has_arg('did')) + $this->_error('No dewar specified'); + + $chkd = $this->db->pq("SELECT d.dewarid FROM dewar d INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE d.dewarid=:1 AND p.proposalid=:2", array($this->arg('did'), $this->proposalid)); + $chkc = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON c.dewarid = d.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE c.containerid=:1 AND p.proposalid=:2", array($this->arg('cid'), $this->proposalid)); + + if (sizeof($chkd) && sizeof($chkc)) { + $this->db->pq("UPDATE container SET dewarid=:1 WHERE containerid=:2", array($this->arg('did'), $this->arg('cid'))); + $this->_output(1); } - - - # Update Container - function _update_container() { - if (!$this->has_arg('cid')) $this->_error('No container specified'); - - $where = 'c.containerid=:1'; - $args = array($this->arg('cid')); - - if (!$this->user->has('disp_cont')) { - $where .= ' AND p.proposalid=:'.(sizeof($args)+1); - array_push($args, $this->proposalid); - } + } - $chkc = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON c.dewarid = d.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); - - if (!sizeof($chkc)) $this->_error('No such container'); - $fields = array('NAME' => 'CODE', 'REQUESTEDRETURN' => 'REQUESTEDRETURN', 'REQUESTEDIMAGERID' => 'REQUESTEDIMAGERID', 'COMMENTS' => 'COMMENTS', 'BARCODE' => 'BARCODE', 'CONTAINERTYPE' => 'CONTAINERTYPE', 'EXPERIMENTTYPE' => 'EXPERIMENTTYPE', 'STORAGETEMPERATURE' => 'STORAGETEMPERATURE', 'CONTAINERREGISTRYID' => 'CONTAINERREGISTRYID', 'PROCESSINGPIPELINEID' => 'PRIORITYPIPELINEID', 'OWNERID' => 'OWNERID'); - foreach ($fields as $k => $f) { - if ($this->has_arg($k)) { - $this->db->pq("UPDATE container SET $f=:1 WHERE containerid=:2", array($this->arg($k), $this->arg('cid'))); - $this->_output(array($k => $this->arg($k))); - } - } + # Update Container + function _update_container() + { + if (!$this->has_arg('cid')) + $this->_error('No container specified'); + + $where = 'c.containerid=:1'; + $args = array($this->arg('cid')); - if ($this->user->has('disp_cont') && $this->has_arg('DISPOSE')) { - $this->db->pq("UPDATE container SET imagerid=NULL,containerstatus='disposed' WHERE containerid=:1", array($this->arg('cid'))); - $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,'disposed')", array($this->arg('cid'))); - $this->_output(array('IMAGERID' => null)); + if (!$this->user->hasPermission('disp_cont')) { + $where .= ' AND p.proposalid=:' . (sizeof($args) + 1); + array_push($args, $this->proposalid); + } + + $chkc = $this->db->pq("SELECT c.containerid FROM container c INNER JOIN dewar d ON c.dewarid = d.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); + + if (!sizeof($chkc)) + $this->_error('No such container'); + + $fields = array('NAME' => 'CODE', 'REQUESTEDRETURN' => 'REQUESTEDRETURN', 'REQUESTEDIMAGERID' => 'REQUESTEDIMAGERID', 'COMMENTS' => 'COMMENTS', 'BARCODE' => 'BARCODE', 'CONTAINERTYPE' => 'CONTAINERTYPE', 'EXPERIMENTTYPE' => 'EXPERIMENTTYPE', 'STORAGETEMPERATURE' => 'STORAGETEMPERATURE', 'CONTAINERREGISTRYID' => 'CONTAINERREGISTRYID', 'PROCESSINGPIPELINEID' => 'PRIORITYPIPELINEID', 'OWNERID' => 'OWNERID'); + foreach ($fields as $k => $f) { + if ($this->has_arg($k)) { + $this->db->pq("UPDATE container SET $f=:1 WHERE containerid=:2", array($this->arg($k), $this->arg('cid'))); + $this->_output(array($k => $this->arg($k))); } } - - - - # Add container - function _add_container() { - if (!$this->has_arg('NAME')) $this->_error('No container name specified'); - if (!$this->has_arg('CONTAINERTYPE')) $this->_error('No container type specified'); - if (!$this->has_arg('DEWARID')) $this->_error('No dewar id specified'); + if ($this->user->hasPermission('disp_cont') && $this->has_arg('DISPOSE')) { + $this->db->pq("UPDATE container SET imagerid=NULL,containerstatus='disposed' WHERE containerid=:1", array($this->arg('cid'))); + $this->db->pq("INSERT INTO containerhistory (containerid,status) VALUES (:1,'disposed')", array($this->arg('cid'))); + $this->_output(array('IMAGERID' => null)); + } + } - $cap = $this->has_arg('CAPACITY') ? $this->arg('CAPACITY') : 16; - $sch = $this->has_arg('SCHEDULEID') ? $this->arg('SCHEDULEID') : null; - $scr = $this->has_arg('SCREENID') ? $this->arg('SCREENID') : null; - $own = $this->has_arg('PERSONID') ? $this->arg('PERSONID') : null; - $rid = $this->has_arg('REQUESTEDIMAGERID') ? $this->arg('REQUESTEDIMAGERID') : null; - $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : null; - $bar = $this->has_arg('BARCODE') ? $this->arg('BARCODE') : null; - $ext = $this->has_arg('EXPERIMENTTYPE') ? $this->arg('EXPERIMENTTYPE') : null; - $tem = $this->has_arg('STORAGETEMPERATURE') ? $this->arg('STORAGETEMPERATURE') : null; - $crid = $this->has_arg('CONTAINERREGISTRYID') ? $this->arg('CONTAINERREGISTRYID') : null; - $pipeline = $this->has_arg('PROCESSINGPIPELINEID') ? $this->arg('PROCESSINGPIPELINEID') : null; + # Add container + function _add_container() + { + if (!$this->has_arg('NAME')) + $this->_error('No container name specified'); + if (!$this->has_arg('CONTAINERTYPE')) + $this->_error('No container type specified'); + if (!$this->has_arg('DEWARID')) + $this->_error('No dewar id specified'); - $this->db->pq("INSERT INTO container (containerid,dewarid,code,bltimestamp,capacity,containertype,scheduleid,screenid,ownerid,requestedimagerid,comments,barcode,experimenttype,storagetemperature,containerregistryid,prioritypipelineid) - VALUES (s_container.nextval,:1,:2,CURRENT_TIMESTAMP,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14) RETURNING containerid INTO :id", - array($this->arg('DEWARID'), $this->arg('NAME'), $cap, $this->arg('CONTAINERTYPE'), $sch, $scr, $own, $rid, $com, $bar, $ext, $tem, $crid, $pipeline)); - $cid = $this->db->id(); + $cap = $this->has_arg('CAPACITY') ? $this->arg('CAPACITY') : 16; + $sch = $this->has_arg('SCHEDULEID') ? $this->arg('SCHEDULEID') : null; + $scr = $this->has_arg('SCREENID') ? $this->arg('SCREENID') : null; + $own = $this->has_arg('PERSONID') ? $this->arg('PERSONID') : null; + $rid = $this->has_arg('REQUESTEDIMAGERID') ? $this->arg('REQUESTEDIMAGERID') : null; + $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : null; + $bar = $this->has_arg('BARCODE') ? $this->arg('BARCODE') : null; + $ext = $this->has_arg('EXPERIMENTTYPE') ? $this->arg('EXPERIMENTTYPE') : null; + $tem = $this->has_arg('STORAGETEMPERATURE') ? $this->arg('STORAGETEMPERATURE') : null; - if ($this->has_arg('SCHEDULEID')) { - $sh = new ImagingShared($this->db); - $sh->_generate_schedule(array( - 'CONTAINERID' => $cid, - 'SCHEDULEID' => $this->arg('SCHEDULEID'), - )); - } + $crid = $this->has_arg('CONTAINERREGISTRYID') ? $this->arg('CONTAINERREGISTRYID') : null; - if ($this->has_arg('AUTOMATED')) { - $this->db->pq("INSERT INTO containerqueue (containerid, personid) VALUES (:1, :2)", array($cid, $this->user->personid)); - } + $pipeline = $this->has_arg('PROCESSINGPIPELINEID') ? $this->arg('PROCESSINGPIPELINEID') : null; - $this->_output(array('CONTAINERID' => $cid)); + $this->db->pq( + "INSERT INTO container (containerid,dewarid,code,bltimestamp,capacity,containertype,scheduleid,screenid,ownerid,requestedimagerid,comments,barcode,experimenttype,storagetemperature,containerregistryid,prioritypipelineid) + VALUES (s_container.nextval,:1,:2,CURRENT_TIMESTAMP,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14) RETURNING containerid INTO :id", + array($this->arg('DEWARID'), $this->arg('NAME'), $cap, $this->arg('CONTAINERTYPE'), $sch, $scr, $own, $rid, $com, $bar, $ext, $tem, $crid, $pipeline) + ); + + $cid = $this->db->id(); + + if ($this->has_arg('SCHEDULEID')) { + $sh = $this->app->container['imagingShared']; + $sh->_generate_schedule(array( + 'CONTAINERID' => $cid, + 'SCHEDULEID' => $this->arg('SCHEDULEID'), + )); } + if ($this->has_arg('AUTOMATED')) { + $this->db->pq("INSERT INTO containerqueue (containerid, personid) VALUES (:1, :2)", array($cid, $this->user->personId)); + } + $this->_output(array('CONTAINERID' => $cid)); + } - function _container_history() { - if (!$this->has_arg('cid') && !$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container specified'); - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; - if ($this->has_arg('all') && ($this->bcr() || $this->staff)) { - $args = array(); - $where = '1=1'; - } + function _container_history() + { + if (!$this->has_arg('cid') && !$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container specified'); - if ($this->has_arg('cid')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('cid')); - } else { - $where .= ' AND c.containerregistryid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERREGISTRYID')); - } + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; - $tot = $this->db->pq("SELECT count(h.containerhistoryid) as tot + if ($this->has_arg('all') && ($this->bcr() || $this->staff)) { + $args = array(); + $where = '1=1'; + } + + if ($this->has_arg('cid')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('cid')); + } else { + $where .= ' AND c.containerregistryid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERREGISTRYID')); + } + + $tot = $this->db->pq("SELECT count(h.containerhistoryid) as tot FROM containerhistory h INNER JOIN container c ON c.containerid = h.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT h.containerhistoryid, s.shippingid, s.shippingname as shipment, CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), b.visit_number) as visit, b.beamlinename as bl, b.beamlineoperator as localcontact, h.containerid, h.status,h.location,TO_CHAR(h.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, h.beamlinename + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT h.containerhistoryid, s.shippingid, s.shippingname as shipment, CONCAT(p.proposalcode, p.proposalnumber, '-', b.visit_number) as visit, b.beamlinename as bl, b.beamlineoperator as localcontact, h.containerid, h.status,h.location,TO_CHAR(h.bltimestamp, 'DD-MM-YYYY HH24:MI') as bltimestamp, h.beamlinename FROM containerhistory h INNER JOIN container c ON c.containerid = h.containerid INNER JOIN dewar d ON d.dewarid = c.dewarid @@ -1712,143 +2094,155 @@ function _container_history() { INNER JOIN proposal p ON p.proposalid = s.proposalid LEFT OUTER JOIN blsession b ON b.sessionid = d.firstexperimentid WHERE $where ORDER BY h.bltimestamp DESC", $args); - - $this->_output(array('total' => $tot, 'data' => $rows)); - } - - /** - * Controller method for add container history endpoint. - * This is called from another service when the container is scanned into a location. - * - * The calling application must be in the bcr whitelist group and include the CONTAINERID and LOCATION in the parameters - * On success, a new entry in ContainerHistory is created with the location updated. - * The ContainerHistory.status can be changed with the optional STATUS argument. - * The ContainerHistory.beamlineName can be changed with the optional BEAMLINE argument. - * It returns error if CONTAINERID does not refer to an existing container. - */ - function _add_container_history() { - if (!$this->bcr()) $this->_error('You need to be on the internal network to add history'); - - if (!$this->has_arg('CONTAINERID') && !$this->has_arg('CODE')) $this->_error('No container id or code specified'); - if (!$this->has_arg('LOCATION')) $this->_error('No location specified'); + $this->_output(array('total' => $tot, 'data' => $rows)); + } - $where = '1=1'; - $args = array(); - if ($this->has_arg('CONTAINERID')) { - $where .= ' AND c.containerid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERID')); - } else if ($this->has_arg('CODE')) { - $where .= ' AND c.code=:'.(sizeof($args)+1); - array_push($args, $this->arg('CODE')); - } + /** + * Controller method for add container history endpoint. + * This is called from another service when the container is scanned into a location. + * + * The calling application must be in the bcr whitelist group and include the CONTAINERID and LOCATION in the parameters + * On success, a new entry in ContainerHistory is created with the location updated. + * The ContainerHistory.status can be changed with the optional STATUS argument. + * The ContainerHistory.beamlineName can be changed with the optional BEAMLINE argument. + * It returns error if CONTAINERID does not refer to an existing container. + */ + function _add_container_history() + { + if (!$this->bcr()) + $this->_error('You need to be on the internal network to add history'); + + if (!$this->has_arg('CONTAINERID') && !$this->has_arg('CODE')) + $this->_error('No container id or code specified'); + if (!$this->has_arg('LOCATION')) + $this->_error('No location specified'); + + $where = '1=1'; + $args = array(); + + if ($this->has_arg('CONTAINERID')) { + $where .= ' AND c.containerid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERID')); + } else if ($this->has_arg('CODE')) { + $where .= ' AND c.code=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CODE')); + } - $cont = $this->db->pq("SELECT c.containerid, d.dewarid, s.shippingid, d.dewarstatus + $cont = $this->db->pq("SELECT c.containerid, d.dewarid, s.shippingid, d.dewarstatus FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping s ON s.shippingid = d.shippingid WHERE $where ORDER BY c.containerid DESC", $args); - if (!sizeof($cont)) $this->_error('No such container', 404); - else $cont = $cont[0]; + if (!sizeof($cont)) + $this->_error('No such container', 404); + else + $cont = $cont[0]; - // We may need to update the dewar and shipping status - for now leave them alone + // We may need to update the dewar and shipping status - for now leave them alone - // Optionally we can set the beamlinename in ContainerHistory - $beamline_name = $this->has_arg('BEAMLINENAME') ? $this->arg('BEAMLINENAME') : null; + // Optionally we can set the beamlinename in ContainerHistory + $beamline_name = $this->has_arg('BEAMLINENAME') ? $this->arg('BEAMLINENAME') : null; - // Figure out the most recent status for this container - // Initially use the dewar status unless its been set previously - $container_status = strtolower($cont['DEWARSTATUS']); + // Figure out the most recent status for this container + // Initially use the dewar status unless its been set previously + $container_status = strtolower($cont['DEWARSTATUS']); - if ($this->has_arg('STATUS')) $container_status = $this->arg('STATUS'); - else { - // Get last status for this container - we are only going to change its location - $container_history = $this->db->pq("SELECT status FROM containerhistory WHERE containerid = :1 ORDER BY containerhistoryid DESC LIMIT 1", array($cont['CONTAINERID'])); + if ($this->has_arg('STATUS')) + $container_status = $this->arg('STATUS'); + else { + // Get last status for this container - we are only going to change its location + $container_history = $this->db->pq("SELECT status FROM containerhistory WHERE containerid = :1 ORDER BY containerhistoryid DESC LIMIT 1", array($cont['CONTAINERID'])); - if (sizeof($container_history)) $container_status = $container_history[0]['STATUS']; - } + if (sizeof($container_history)) + $container_status = $container_history[0]['STATUS']; + } - // Insert new record to store container location - $this->db->pq("INSERT INTO containerhistory (containerid,status,location,beamlinename) VALUES (:1,:2,:3,:4)", array($cont['CONTAINERID'], $container_status, $this->arg('LOCATION'), $beamline_name)); - $chid = $this->db->id(); + // Insert new record to store container location + $this->db->pq("INSERT INTO containerhistory (containerid,status,location,beamlinename) VALUES (:1,:2,:3,:4)", array($cont['CONTAINERID'], $container_status, $this->arg('LOCATION'), $beamline_name)); + $chid = $this->db->id(); - // Update the container beamlinelocation so we can filter out duplicate container history entries - $this->db->pq("UPDATE container SET beamlinelocation=:1 WHERE containerid=:2", array($this->arg('LOCATION'), $cont['CONTAINERID'])); + // Update the container beamlinelocation so we can filter out duplicate container history entries + $this->db->pq("UPDATE container SET beamlinelocation=:1 WHERE containerid=:2", array($this->arg('LOCATION'), $cont['CONTAINERID'])); - $this->_output(array('CONTAINERHISTORYID' => $chid)); - } + $this->_output(array('CONTAINERHISTORYID' => $chid)); + } - function _get_container_types() { - $where = ''; - // By default only return active container types. - // If all param set return everything - if ($this->has_arg('all')) { - $where .= '1=1'; - } else { - $where .= 'ct.active = 1'; - } - $rows = $this->db->pq("SELECT ct.containerTypeId, name, ct.proposalType, ct.capacity, ct.wellPerRow, ct.dropPerWellX, ct.dropPerWellY, ct.dropHeight, ct.dropWidth, ct.wellDrop FROM ContainerType ct WHERE $where"); - $this->_output(array('total' => count($rows), 'data' => $rows)); + function _get_container_types() + { + $where = ''; + // By default only return active container types. + // If all param set return everything + if ($this->has_arg('all')) { + $where .= '1=1'; + } else { + $where .= 'ct.active = 1'; } + $rows = $this->db->pq("SELECT ct.containerTypeId, name, ct.proposalType, ct.capacity, ct.wellPerRow, ct.dropPerWellX, ct.dropPerWellY, ct.dropHeight, ct.dropWidth, ct.wellDrop FROM ContainerType ct WHERE $where"); + $this->_output(array('total' => count($rows), 'data' => $rows)); + } - function _container_registry() { - $args = array($this->proposalid); - $where = 'p.proposalid=:1'; + function _container_registry() + { + $args = array($this->proposalid); + $where = 'p.proposalid=:1'; - if ($this->has_arg('all') && $this->staff) { - $args = array(); - $where = '1=1'; - } + if ($this->has_arg('all') && $this->staff) { + $args = array(); + $where = '1=1'; + } - if ($this->has_arg('CONTAINERREGISTRYID')) { - $where .= ' AND r.containerregistryid=:'.(sizeof($args)+1); - array_push($args, $this->arg('CONTAINERREGISTRYID')); - } + if ($this->has_arg('CONTAINERREGISTRYID')) { + $where .= ' AND r.containerregistryid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('CONTAINERREGISTRYID')); + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(r.barcode) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) OR lower(r.comments) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."), '%')))"; - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - array_push($args, $this->arg('s')); - } + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND (lower(r.barcode) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) OR lower(r.comments) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 1) . "), '%')) OR lower(CONCAT(p.proposalcode,p.proposalnumber)) LIKE lower(CONCAT(CONCAT('%',:" . ($st + 2) . "), '%')))"; + array_push($args, $this->arg('s')); + array_push($args, $this->arg('s')); + array_push($args, $this->arg('s')); + } - if ($this->has_arg('t')) { - if ($this->arg('t') == 'orphan') $where .= " AND rhp.containerregistryid IS NULL"; - } + if ($this->has_arg('t')) { + if ($this->arg('t') == 'orphan') + $where .= " AND rhp.containerregistryid IS NULL"; + } - $tot = $this->db->pq("SELECT count(r.containerregistryid) as tot + $tot = $this->db->pq("SELECT count(r.containerregistryid) as tot FROM containerregistry r LEFT OUTER JOIN containerregistry_has_proposal rhp on rhp.containerregistryid = r.containerregistryid LEFT OUTER JOIN proposal p ON p.proposalid = rhp.proposalid WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $order = 'r.barcode'; - if ($this->has_arg('sort_by')) { - $cols = array( - 'BARCODE' => 'r.barcode', 'INSTANCES' => 'count(distinct c.containerid)', - 'LASTUSE' => 'max(c.bltimestamp)', 'RECORDTIMESTAMP' => 'r.recordtimestamp', - 'REPORTS' => 'count(cr.containerreportid)' - ); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $order = 'r.barcode'; + if ($this->has_arg('sort_by')) { + $cols = array( + 'BARCODE' => 'r.barcode', 'INSTANCES' => 'count(distinct c.containerid)', + 'LASTUSE' => 'max(c.bltimestamp)', 'RECORDTIMESTAMP' => 'r.recordtimestamp', + 'REPORTS' => 'count(cr.containerreportid)' + ); + $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; + if (array_key_exists($this->arg('sort_by'), $cols)) + $order = $cols[$this->arg('sort_by')] . ' ' . $dir; + } - $rows = $this->db->paginate("SELECT r.containerregistryid, r.barcode, GROUP_CONCAT(distinct CONCAT(p.proposalcode,p.proposalnumber) SEPARATOR ', ') as proposals, count(distinct c.containerid) as instances, TO_CHAR(r.recordtimestamp, 'DD-MM-YYYY') as recordtimestamp, + $rows = $this->db->paginate("SELECT r.containerregistryid, r.barcode, GROUP_CONCAT(distinct CONCAT(p.proposalcode,p.proposalnumber) SEPARATOR ', ') as proposals, count(distinct c.containerid) as instances, TO_CHAR(r.recordtimestamp, 'DD-MM-YYYY') as recordtimestamp, TO_CHAR(max(c.bltimestamp),'DD-MM-YYYY') as lastuse, max(CONCAT(p.proposalcode,p.proposalnumber)) as prop, r.comments, COUNT(distinct cr.containerreportid) as reports FROM containerregistry r LEFT OUTER JOIN containerregistry_has_proposal rhp on rhp.containerregistryid = r.containerregistryid @@ -1860,121 +2254,145 @@ function _container_registry() { GROUP BY r.containerregistryid ORDER BY $order", $args); - - if ($this->has_arg('CONTAINERREGISTRYID')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such container'); - } else $this->_output(array('total' => $tot, 'data' => $rows)); - } + if ($this->has_arg('CONTAINERREGISTRYID')) { + if (sizeof($rows)) + $this->_output($rows[0]); + else + $this->_error('No such container'); + } else + $this->_output(array('total' => $tot, 'data' => $rows)); + } - function _add_container_registry() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('BARCODE')) $this->_error('No barcode specified'); + function _add_container_registry() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('BARCODE')) + $this->_error('No barcode specified'); - $chk = $this->db->pq("SELECT containerregistryid + $chk = $this->db->pq("SELECT containerregistryid FROM containerregistry WHERE lower(barcode) LIKE lower(:1)", array($this->arg('BARCODE'))); - if (sizeof($chk)) $this->_error('That barcode is already registered'); + if (sizeof($chk)) + $this->_error('That barcode is already registered'); - $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : ''; + $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : ''; - $this->db->pq("INSERT INTO containerregistry (barcode,comments) VALUES (:1,:2)", array($this->arg('BARCODE'), $com)); - $this->_output(array('CONTAINERREGISTRYID' => $this->db->id())); - } + $this->db->pq("INSERT INTO containerregistry (barcode,comments) VALUES (:1,:2)", array($this->arg('BARCODE'), $com)); + $this->_output(array('CONTAINERREGISTRYID' => $this->db->id())); + } - function _update_container_registry() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container id specified'); - - $fields = array('COMMENTS'); - foreach ($fields as $f) { - if ($this->has_arg($f)) { - $this->db->pq("UPDATE containerregistry SET $f=:1 WHERE containerregistryid=:2", array($this->arg($f), $this->arg('CONTAINERREGISTRYID'))); - $this->_output(array($f => $this->arg($f))); - } + function _update_container_registry() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container id specified'); + + $fields = array('COMMENTS'); + foreach ($fields as $f) { + if ($this->has_arg($f)) { + $this->db->pq("UPDATE containerregistry SET $f=:1 WHERE containerregistryid=:2", array($this->arg($f), $this->arg('CONTAINERREGISTRYID'))); + $this->_output(array($f => $this->arg($f))); } } + } - function _get_prop_container() { - if (!$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container specified'); + function _get_prop_container() + { + if (!$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container specified'); - $rows = $this->db->pq("SELECT crhp.containerregistryhasproposalid,crhp.containerregistryid,crhp.proposalid,CONCAT(p.proposalcode, p.proposalnumber) as proposal + $rows = $this->db->pq("SELECT crhp.containerregistryhasproposalid,crhp.containerregistryid,crhp.proposalid,CONCAT(p.proposalcode, p.proposalnumber) as proposal FROM containerregistry_has_proposal crhp INNER JOIN proposal p ON p.proposalid = crhp.proposalid WHERE crhp.containerregistryid = :1", array($this->arg('CONTAINERREGISTRYID'))); - $this->_output($rows); - } + $this->_output($rows); + } - function _add_prop_container() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container specified'); - if (!$this->has_arg('PROPOSALID')) $this->_error('No proposal specified'); + function _add_prop_container() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container specified'); + if (!$this->has_arg('PROPOSALID')) + $this->_error('No proposal specified'); - $chk = $this->db->pq("SELECT containerregistryid FROM containerregistry_has_proposal + $chk = $this->db->pq("SELECT containerregistryid FROM containerregistry_has_proposal WHERE containerregistryid = :1 AND proposalid = :2", array($this->arg('CONTAINERREGISTRYID'), $this->arg('PROPOSALID'))); - if (sizeof($chk)) $this->_error('That container is already registered to that proposal'); - - $this->db->pq("INSERT INTO containerregistry_has_proposal (containerregistryid,proposalid,personid) - VALUES (:1,:2,:3)", array($this->arg('CONTAINERREGISTRYID'), $this->arg('PROPOSALID'), $this->user->personid)); + if (sizeof($chk)) + $this->_error('That container is already registered to that proposal'); - $this->_output(array('CONTAINERREGISTRYHASPROPOSALID' => $this->db->id())); + $this->db->pq("INSERT INTO containerregistry_has_proposal (containerregistryid,proposalid,personid) + VALUES (:1,:2,:3)", array($this->arg('CONTAINERREGISTRYID'), $this->arg('PROPOSALID'), $this->user->personId)); - } + $this->_output(array('CONTAINERREGISTRYHASPROPOSALID' => $this->db->id())); + } - function _rem_prop_container() { - if (!$this->staff) $this->_error('No access'); - if (!$this->has_arg('CONTAINERREGISTRYHASPROPOSALID')) $this->_error('No container proposal specified'); + function _rem_prop_container() + { + if (!$this->staff) + $this->_error('No access'); + if (!$this->has_arg('CONTAINERREGISTRYHASPROPOSALID')) + $this->_error('No container proposal specified'); - $this->db->pq("DELETE FROM containerregistry_has_proposal WHERE containerregistryhasproposalid=:1", array($this->arg('CONTAINERREGISTRYHASPROPOSALID'))); - } + $this->db->pq("DELETE FROM containerregistry_has_proposal WHERE containerregistryhasproposalid=:1", array($this->arg('CONTAINERREGISTRYHASPROPOSALID'))); + } - function _get_container_reports() { - if (!$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container specified'); + function _get_container_reports() + { + if (!$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container specified'); - $where = 'r.containerregistryid=:1'; - $args = array($this->arg('CONTAINERREGISTRYID')); + $where = 'r.containerregistryid=:1'; + $args = array($this->arg('CONTAINERREGISTRYID')); - $tot = $this->db->pq("SELECT count(r.containerregistryid) as tot + $tot = $this->db->pq("SELECT count(r.containerregistryid) as tot FROM containerreport r WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - - $st = sizeof($args)+1; - $en = $st + 1; - array_push($args, $start); - array_push($args, $end); - - $rows = $this->db->paginate("SELECT r.containerreportid, r.report, TO_CHAR(r.recordtimestamp, 'HH24:MI DD-MM-YYYY') as recordtimestamp, IF(r.attachmentfilepath IS NOT NULL,1,0) as attachment, CONCAT(p.givenname, ' ', p.familyname) as reporter + $tot = intval($tot[0]['TOT']); + + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page') - 1 : 0; + $start = $pg * $pp; + $end = $pg * $pp + $pp; + + $st = sizeof($args) + 1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $rows = $this->db->paginate("SELECT r.containerreportid, r.report, TO_CHAR(r.recordtimestamp, 'HH24:MI DD-MM-YYYY') as recordtimestamp, IF(r.attachmentfilepath IS NOT NULL,1,0) as attachment, CONCAT(p.givenname, ' ', p.familyname) as reporter FROM containerreport r INNER JOIN person p ON p.personid = r.personid WHERE $where ORDER BY r.recordtimestamp DESC", $args); - foreach ($rows as $i => &$row) { - $row['REPORT'] = $this->db->read($row['REPORT']); - } - - $this->_output(array('total' => $tot, 'data' => $rows)); + foreach ($rows as $i => &$row) { + $row['REPORT'] = $this->db->read($row['REPORT']); } - function _add_container_report() { - if (!$this->has_arg('REPORT')) $this->_error('No report specified'); - if (!$this->has_arg('CONTAINERREGISTRYID')) $this->_error('No container specified'); + $this->_output(array('total' => $tot, 'data' => $rows)); + } + + function _add_container_report() + { + if (!$this->has_arg('REPORT')) + $this->_error('No report specified'); + if (!$this->has_arg('CONTAINERREGISTRYID')) + $this->_error('No container specified'); - $last_visits = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename, s.beamlineoperator as localcontact, pe.emailaddress, r.containerregistryid, r.barcode, CONCAT(p.proposalcode, p.proposalnumber) as prop + $last_visits = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename, s.beamlineoperator as localcontact, pe.emailaddress, r.containerregistryid, r.barcode, CONCAT(p.proposalcode, p.proposalnumber) as prop FROM containerregistry r INNER JOIN container c ON c.containerregistryid = r.containerregistryid LEFT OUTER JOIN person pe ON pe.personid = c.ownerid @@ -1986,290 +2404,458 @@ function _add_container_report() { ORDER BY c.containerid DESC LIMIT 1", array($this->arg('CONTAINERREGISTRYID'))); - if (!sizeof($last_visits)) $this->_error('Cant find a visit for that container'); - else $lv = $last_visits[0]; + if (!sizeof($last_visits)) + $this->_error('Cant find a visit for that container'); + else + $lv = $last_visits[0]; - $path = null; - if (array_key_exists('ATTACHMENT', $_FILES)) { - if ($_FILES['ATTACHMENT']['name']) { - $info = pathinfo($_FILES['ATTACHMENT']['name']); + $path = null; + if (array_key_exists('ATTACHMENT', $_FILES)) { + if ($_FILES['ATTACHMENT']['name']) { + $info = pathinfo($_FILES['ATTACHMENT']['name']); - if ($info['extension'] == 'jpg' || $info['extension'] == 'jpeg') { - # dls_mxweb cant write to visits... - #$root = '/dls/'.$lv['BEAMLINENAME'].'/data/'.$lv['YEAR'].'/'.$lv['VISIT'].'/.ispyb/'; + if ($info['extension'] == 'jpg' || $info['extension'] == 'jpeg') { + # dls_mxweb cant write to visits... + #$root = '/dls/'.$lv['BEAMLINENAME'].'/data/'.$lv['YEAR'].'/'.$lv['VISIT'].'/.ispyb/'; - $root = '/dls_sw/dasc/ispyb2/uploads/'.$lv['YEAR'].'/'.$lv['VISIT'].'/'; - if (!is_dir($root)) { - mkdir($root, 0755, true); - } + $root = '/dls_sw/dasc/ispyb2/uploads/' . $lv['YEAR'] . '/' . $lv['VISIT'] . '/'; + if (!is_dir($root)) { + mkdir($root, 0755, true); + } - $file = strftime('%Y-%m-%d_%H%M').'containerreport.jpg'; - $path = $root.$file; + $file = strftime('%Y-%m-%d_%H%M') . 'containerreport.jpg'; + $path = $root . $file; - move_uploaded_file($_FILES['ATTACHMENT']['tmp_name'], $path); - } + move_uploaded_file($_FILES['ATTACHMENT']['tmp_name'], $path); } } + } - $this->db->pq("INSERT INTO containerreport (containerregistryid,report,attachmentfilepath,personid,recordtimestamp) VALUES (:1,:2,:3,:4,CURRENT_TIMESTAMP)", - array($this->arg('CONTAINERREGISTRYID'), $this->arg('REPORT'), $path, $this->user->personid)); - if ($lv['EMAILADDRESS']) { - $recpts = array($lv['EMAILADDRESS']); - $local = $this->_get_email_fn($lv['LOCALCONTACT']); - if ($local) array_push($recpts, $local); - - $lv['NOW'] = strftime('%d-%m-%Y %H:%M'); - $lv['REPORT'] = $this->arg('REPORT'); - $email = new Email('container-report', '*** Status Report for Container '.$lv['BARCODE'].' at '.$lv['NOW'].' ***'); - $email->data = $lv; - $email->send(implode(', ', $recpts)); - } + $this->db->pq( + "INSERT INTO containerreport (containerregistryid,report,attachmentfilepath,personid,recordtimestamp) VALUES (:1,:2,:3,:4,CURRENT_TIMESTAMP)", + array($this->arg('CONTAINERREGISTRYID'), $this->arg('REPORT'), $path, $this->user->personId) + ); + if ($lv['EMAILADDRESS']) { + $recpts = array($lv['EMAILADDRESS']); + $local = $this->_get_email_fn($lv['LOCALCONTACT']); + if ($local) + array_push($recpts, $local); + + $lv['NOW'] = strftime('%d-%m-%Y %H:%M'); + $lv['REPORT'] = $this->arg('REPORT'); + $email = new Email('container-report', '*** Status Report for Container ' . $lv['BARCODE'] . ' at ' . $lv['NOW'] . ' ***'); + $email->data = $lv; + $email->send(implode(', ', $recpts)); + } - $this->_output(array('CONTAINERREPORTID' => $this->db->id())); - } + $this->_output(array('CONTAINERREPORTID' => $this->db->id())); + } + # Cache form temporary data to session + function _session_cache() + { + $data = array_key_exists('data', $this->request) ? $this->request['data'] : null; + if (!$this->has_arg('name') || !$data) + $this->_error('No key and data specified'); + $this->user->setInCache($this->arg('name'), $data); + $this->_output(array('data' => $data)); + } - - - # Cache form temporary data to session - function _session_cache() { - $data = array_key_exists('data', $this->request) ? $this->request['data'] : null; - if (!$this->has_arg('name') || !$data) $this->_error('No key and data specified'); + function _get_session_cache() + { + if (!$this->has_arg('name')) + $this->_error('No key specified'); + $this->_output($this->user->getFromCache($this->arg('name'))); + } + + function _dummy_shipment_put() + { + // Does nothing when a PUT request is made on the shipment endpoint + // this can happen when a user edits a labcontact and selects the current labcontact + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); + $sid = $this->arg('sid'); + $this->_output(array('SHIPPINGID' => $sid)); + } - $this->user->set_cache($this->arg('name'), $data); - $this->_output(array('data' => $data)); + # Ajax shipment registration + function _add_shipment() + { + global $dewar_weight; + if (!$this->has_arg('prop')) + $this->_error('No proposal specified', 'Please select a proposal first'); + if (!$this->has_arg('SHIPPINGNAME')) + $this->_error('No shipment name specified', 'Please specify a shipment name'); + + $an = $this->has_arg('DELIVERYAGENT_AGENTNAME') ? $this->arg('DELIVERYAGENT_AGENTNAME') : ''; + $ac = $this->has_arg('DELIVERYAGENT_AGENTCODE') ? $this->arg('DELIVERYAGENT_AGENTCODE') : ''; + + $sd = $this->has_arg('DELIVERYAGENT_SHIPPINGDATE') ? $this->arg('DELIVERYAGENT_SHIPPINGDATE') : ''; + $dd = $this->has_arg('DELIVERYAGENT_DELIVERYDATE') ? $this->arg('DELIVERYAGENT_DELIVERYDATE') : ''; + $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : ''; + + $rt = $this->has_arg('READYBYTIME') ? $this->arg('READYBYTIME') : null; + $ct = $this->has_arg('CLOSETIME') ? $this->arg('CLOSETIME') : null; + $loc = $this->has_arg('PHYSICALLOCATION') ? $this->arg('PHYSICALLOCATION') : null; + + $hard_drive_enclosed = null; + if ($this->has_arg('ENCLOSEDHARDDRIVE')) { + $hard_drive_enclosed = $this->arg('ENCLOSEDHARDDRIVE') ? "Yes" : "No"; } - - - function _get_session_cache() { - if (!$this->has_arg('name')) $this->_error('No key specified'); - $this->_output($this->user->cache($this->arg('name'))); + + $tools_enclosed = null; + if ($this->has_arg('ENCLOSEDTOOLS')) { + $tools_enclosed = $this->arg('ENCLOSEDTOOLS') ? "Yes" : "No"; } - - - # Ajax shipment registration - function _add_shipment() { - global $dewar_weight; - if (!$this->has_arg('prop')) $this->_error('No proposal specified', 'Please select a proposal first'); - - if (!$this->has_arg('SHIPPINGNAME')) $this->_error('No shipment name specified', 'Please specify a shipment name'); - - $an = $this->has_arg('DELIVERYAGENT_AGENTNAME') ? $this->arg('DELIVERYAGENT_AGENTNAME') : ''; - $ac = $this->has_arg('DELIVERYAGENT_AGENTCODE') ? $this->arg('DELIVERYAGENT_AGENTCODE') : ''; - - $sd = $this->has_arg('DELIVERYAGENT_SHIPPINGDATE') ? $this->arg('DELIVERYAGENT_SHIPPINGDATE') : ''; - $dd = $this->has_arg('DELIVERYAGENT_DELIVERYDATE') ? $this->arg('DELIVERYAGENT_DELIVERYDATE') : ''; - $com = $this->has_arg('COMMENTS') ? $this->arg('COMMENTS') : ''; - - $rt = $this->has_arg('READYBYTIME') ? $this->arg('READYBYTIME') : null; - $ct = $this->has_arg('CLOSETIME') ? $this->arg('CLOSETIME') : null; - $loc = $this->has_arg('PHYSICALLOCATION') ? $this->arg('PHYSICALLOCATION') : null; - - - $this->db->pq("INSERT INTO shipping (shippingid, proposalid, shippingname, deliveryagent_agentname, deliveryagent_agentcode, deliveryagent_shippingdate, deliveryagent_deliverydate, bltimestamp, creationdate, comments, sendinglabcontactid, returnlabcontactid, shippingstatus, safetylevel, readybytime, closetime, physicallocation) - VALUES (s_shipping.nextval,:1,:2,:3,:4,TO_DATE(:5,'DD-MM-YYYY'), TO_DATE(:6,'DD-MM-YYYY'),CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,:7,:8,:9,'opened',:10, :11, :12, :13) RETURNING shippingid INTO :id", - array($this->proposalid, $this->arg('SHIPPINGNAME'), $an, $ac, $sd, $dd, $com, $this->arg('SENDINGLABCONTACTID'), $this->arg('RETURNLABCONTACTID'), $this->arg('SAFETYLEVEL'), $rt, $ct, $loc)); - - $sid = $this->db->id(); - - if ($this->has_arg('DEWARS')) { - if ($this->arg('DEWARS') > 0) { - $first_experiment = ''; - if ($this->has_arg('FIRSTEXPERIMENTID')) { - $first_experiment = $this->arg('FIRSTEXPERIMENTID'); - } - $exp = !empty($first_experiment) ? $this->arg('FIRSTEXPERIMENTID') : null; + $dynamic = null; + if ($this->has_arg('DYNAMIC')) { + $dynamic = $this->arg("DYNAMIC") ? "Yes" : "No"; + } - if ($exp) { - $this->db->pq("INSERT INTO shippinghassession (shippingid, sessionid) + $extra_array = array( + "ENCLOSEDHARDDRIVE" => $hard_drive_enclosed, + "ENCLOSEDTOOLS" => $tools_enclosed, + "DYNAMIC" => $dynamic, + ); + + if ($dynamic) { + $remote_or_mailin = $this->has_arg('REMOTEORMAILIN') ? $this->arg('REMOTEORMAILIN') : ''; + $session_length = $this->has_arg('SESSIONLENGTH') ? $this->arg('SESSIONLENGTH') : ''; + $energy_requirements = $this->has_arg('ENERGY') ? $this->arg('ENERGY') : ''; + $microfocus_beam = null; + if ($this->has_arg('MICROFOCUSBEAM')) { + $microfocus_beam = $this->arg('MICROFOCUSBEAM') ? "Yes" : "No"; + } + $scheduling_restrictions = null; + if ($this->has_arg('SCHEDULINGRESTRICTIONS')) { + $scheduling_restrictions = $this->arg('SCHEDULINGRESTRICTIONS') ? $this->arg('SCHEDULINGRESTRICTIONS') : "None"; + } + $last_minute_beamtime = null; + if ($this->has_arg('LASTMINUTEBEAMTIME')) { + $last_minute_beamtime = $this->arg('LASTMINUTEBEAMTIME') ? "Yes" : "No"; + } + $dewar_grouping = $this->has_arg('DEWARGROUPING') ? $this->arg('DEWARGROUPING') : ''; + $extra_support_requirement = $this->has_arg('EXTRASUPPORTREQUIREMENT') ? $this->arg('EXTRASUPPORTREQUIREMENT') : ''; + $multi_axis_goniometry = null; + if ($this->has_arg('MULTIAXISGONIOMETRY')) { + $multi_axis_goniometry = $this->arg('MULTIAXISGONIOMETRY') ? "Yes" : "No"; + } + $dynamic_options = array( + "REMOTEORMAILIN" => $remote_or_mailin, + "SESSIONLENGTH" => $session_length, + "ENERGY" => $energy_requirements, + "MICROFOCUSBEAM" => $microfocus_beam, + "SCHEDULINGRESTRICTIONS" => $scheduling_restrictions, + "LASTMINUTEBEAMTIME" => $last_minute_beamtime, + "DEWARGROUPING" => $dewar_grouping, + "EXTRASUPPORTREQUIREMENT" => $extra_support_requirement, + "MULTIAXISGONIOMETRY" => $multi_axis_goniometry + ); + + $extra_array = array_merge($extra_array, $dynamic_options); + } + + $extra = json_encode($extra_array); + + $this->db->pq( + "INSERT INTO shipping (shippingid, proposalid, shippingname, deliveryagent_agentname, deliveryagent_agentcode, deliveryagent_shippingdate, deliveryagent_deliverydate, bltimestamp, creationdate, comments, sendinglabcontactid, returnlabcontactid, shippingstatus, safetylevel, readybytime, closetime, physicallocation) + VALUES (s_shipping.nextval,:1,:2,:3,:4,TO_DATE(:5,'DD-MM-YYYY'), TO_DATE(:6,'DD-MM-YYYY'),CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,:7,:8,:9,'opened',:10, :11, :12, :13) RETURNING shippingid INTO :id", + array($this->proposalid, $this->arg('SHIPPINGNAME'), $an, $ac, $sd, $dd, $com, $this->arg('SENDINGLABCONTACTID'), $this->arg('RETURNLABCONTACTID'), $this->arg('SAFETYLEVEL'), $rt, $ct, $loc) + ); + + $sid = $this->db->id(); + + $this->db->pq("UPDATE shipping SET extra=:1 WHERE shippingid=:2", array($extra, $sid)); + + if ($this->has_arg('DEWARS')) { + if ($this->arg('DEWARS') > 0) { + $first_experiment = ''; + if ($this->has_arg('FIRSTEXPERIMENTID')) { + $first_experiment = $this->arg('FIRSTEXPERIMENTID'); + } + + $exp = !empty($first_experiment) ? $this->arg('FIRSTEXPERIMENTID') : null; + + if ($exp) { + $this->db->pq("INSERT INTO shippinghassession (shippingid, sessionid) VALUES (:1,:2)", array($sid, $exp)); + } + + $fcs = $this->arg('FCODES'); + for ($i = 0; $i < $this->arg('DEWARS'); $i++) { + $fc = $i < sizeof($this->arg('FCODES')) ? $fcs[$i] : ''; + $n = $fc ? $fc : ('Dewar' . ($i + 1)); + + $this->db->pq( + "INSERT INTO dewar (dewarid,code,shippingid,bltimestamp,dewarstatus,firstexperimentid,facilitycode,weight) + VALUES (s_dewar.nextval,:1,:2,CURRENT_TIMESTAMP,'opened',:3,:4,$dewar_weight) RETURNING dewarid INTO :id", + array($n, $sid, $exp, $fc) + ); + + $id = $this->db->id(); + + $vis = ''; + if ($exp) { + $vr = $this->db->pq("SELECT s.beamlinename as bl,s.visit_number as vis FROM blsession s WHERE s.sessionid=:1", array($exp)); + if (sizeof($vr)) + $vis = '-' . $vr[0]['VIS'] . '-' . $vr[0]['BL']; } - - $fcs = $this->arg('FCODES'); - for ($i = 0; $i < $this->arg('DEWARS'); $i++) { - $fc = $i < sizeof($this->arg('FCODES')) ? $fcs[$i] : ''; - $n = $fc ? $fc : ('Dewar'.($i+1)); - - $this->db->pq("INSERT INTO dewar (dewarid,code,shippingid,bltimestamp,dewarstatus,firstexperimentid,facilitycode,weight) - VALUES (s_dewar.nextval,:1,:2,CURRENT_TIMESTAMP,'opened',:3,:4,$dewar_weight) RETURNING dewarid INTO :id", - array($n, $sid, $exp, $fc)); - - $id = $this->db->id(); - - $vis = ''; - if ($exp) { - $vr = $this->db->pq("SELECT s.beamlinename as bl,s.visit_number as vis FROM blsession s WHERE s.sessionid=:1", array($exp)); - if (sizeof($vr)) $vis = '-'.$vr[0]['VIS'].'-'.$vr[0]['BL']; - } - - $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($this->arg('prop').$vis.'-'.str_pad($id,7,'0',STR_PAD_LEFT), $id)); - } + + $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($this->arg('prop') . $vis . '-' . str_pad($id, 7, '0', STR_PAD_LEFT), $id)); } } - - $this->_output(array('SHIPPINGID' => $sid)); - } - - function _get_default_dewar() { - if (!$this->has_arg('visit')) $this->_error('No visit specified'); - - $sids = $this->db->pq("SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1 AND p.proposalid=:2", array($this->arg('visit'), $this->proposalid)); - - if (!sizeof($sids)) $this->_error('No such visit'); - else $sid = $sids[0]['SESSIONID']; - - - $shids = $this->db->pq("SELECT shippingid FROM shipping WHERE proposalid LIKE :1 AND shippingname LIKE :2", array($this->proposalid, $this->arg('visit').'_Shipment1')); - - if (sizeof($shids) > 0) { - $shid = $shids[0]['SHIPPINGID']; - } else { - $this->db->pq("INSERT INTO shipping (shippingid,proposalid,shippingname,bltimestamp,creationdate,shippingstatus) VALUES (s_shipping.nextval,:1,:2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'processing') RETURNING shippingid INTO :id", array($this->proposalid, $this->arg('visit').'_Shipment1')); - - $shid = $this->db->id(); - + } + + $this->_output(array('SHIPPINGID' => $sid)); + } + + function _get_default_dewar() + { + if ($this->has_arg('visit')) { + $shipmentName = $this->arg('visit') . '_Shipment1'; + $dewarName = $this->arg('visit') . '_Dewar1'; + $sids = $this->db->pq("SELECT s.sessionid FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1 AND p.proposalid=:2", array($this->arg('visit'), $this->proposalid)); + if (!sizeof($sids)) + $this->_error('No such visit'); + else + $sid = $sids[0]['SESSIONID']; + + } elseif ($this->has_arg('prop')) { + $shipmentName = $this->arg('prop') . '_Shipment1'; + $dewarName = $this->arg('prop') . '_Dewar1'; + $sid = null; + } else { + $this->_error('No visit or proposal specified'); + } + + $shids = $this->db->pq("SELECT shippingid FROM shipping WHERE proposalid LIKE :1 AND shippingname LIKE :2", array($this->proposalid, $shipmentName)); + + if (sizeof($shids) > 0) { + $shid = $shids[0]['SHIPPINGID']; + } else { + $this->db->pq("INSERT INTO shipping (shippingid,proposalid,shippingname,bltimestamp,creationdate,shippingstatus) VALUES (s_shipping.nextval,:1,:2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'processing') RETURNING shippingid INTO :id", array($this->proposalid, $shipmentName)); + + $shid = $this->db->id(); + if ($sid) $vals = $this->db->pq("INSERT INTO shippinghassession (shippingid,sessionid) VALUES (:1,:2)", array($shid, $sid)); - - } - - $did = -1; - if ($sid) { - $dids = $this->db->pq("SELECT dewarid from dewar WHERE shippingid LIKE :1 AND code LIKE :2", array($shid, $this->arg('visit').'_Dewar1')); - - if (sizeof($dids) > 0) { - $did = $dids[0]['DEWARID']; - - } else { - $this->db->pq("INSERT INTO dewar (dewarid,code,shippingid,bltimestamp,dewarstatus,firstexperimentid) VALUES (s_dewar.nextval,:1,:2,CURRENT_TIMESTAMP,'processing',:3) RETURNING dewarid INTO :id", array($this->arg('visit').'_Dewar1', $shid, $sid)); - - $did = $this->db->id(); + } - # Need to generate barcode - $bl = $this->db->pq("SELECT s.beamlinename as bl FROM blsession s WHERE s.sessionid=:1", array($sid)); - $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($this->arg('visit').'-'.$bl[0]['BL'].'-'.str_pad($did,7,'0',STR_PAD_LEFT), $did)); - } + $did = -1; + + $dids = $this->db->pq("SELECT dewarid from dewar WHERE shippingid LIKE :1 AND code LIKE :2", array($shid, $dewarName)); + + if (sizeof($dids) > 0) { + $did = $dids[0]['DEWARID']; + } else { + $this->db->pq("INSERT INTO dewar (dewarid,code,shippingid,bltimestamp,dewarstatus,firstexperimentid) VALUES (s_dewar.nextval,:1,:2,CURRENT_TIMESTAMP,'processing',:3) RETURNING dewarid INTO :id", array($dewarName, $shid, $sid)); + + $did = $this->db->id(); + + # Need to generate barcode + if ($this->has_arg('visit')) { + $bl = $this->db->pq("SELECT s.beamlinename as bl FROM blsession s WHERE s.sessionid=:1", array($sid)); + $barcode = $this->arg('visit') . '-' . $bl[0]['BL'] . '-' . str_pad($did, 7, '0', STR_PAD_LEFT); + } else { + $barcode = $this->arg('prop') . '-' . str_pad($did, 7, '0', STR_PAD_LEFT); } - - if ($did == -1) $this->_error('Couldn\'t create default dewar'); - $this->_output($did); + $this->db->pq("UPDATE dewar set barcode=:1 WHERE dewarid=:2", array($barcode, $did)); } - function _create_awb() { - global $dhl_service, $dhl_service_eu, $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde; + if ($did == -1) + $this->_error('Couldn\'t create default dewar'); + $this->_output($did); + } + + + function _book_shipment_in_shipping_service($user, $shipment, $dewars, $journey_type) { + $address_lines = explode(PHP_EOL, rtrim($user["address"])); + $contact = array( + "company_name" => $user["company"], + "address_line1" => isset($address_lines[0]) ? $address_lines[0] : null, + "address_line2" => isset($address_lines[1]) ? $address_lines[1] : null, + "address_line3" => isset($address_lines[2]) ? $address_lines[2] : null, + "city" => $user["city"], + "country" => $user["country"], + "post_code" => rtrim($user["postcode"]), + "contact_name" => $user["name"], + "contact_phone_number" => $user["phone"], + "contact_email" => rtrim($user["email"]) + ); + $shipment_data = array( + "shipment_reference" => $shipment["PROP"], + "external_id" => $shipment['SHIPPINGID'], + "packages" => array_map( + function($dewar) {return array("external_id" => $dewar["DEWARID"]);}, + $dewars + ) + ); + + // Create or update shipment in shipping service + try { + $response = $this->shipping_service->get_shipment($shipment['SHIPPINGID'], $journey_type); + $user_shipment_role = $this->has_arg('RETURN') ? "consignee" : "shipper"; + $relabelled_contact = array_combine( + array_map(function($key) use ($user_shipment_role) {return $user_shipment_role."_".$key;}, + array_keys($contact)), + $contact); + $shipment_update_data = array_merge($response, $shipment_data, $relabelled_contact); + $this->shipping_service->update_shipment($shipment["SHIPPINGID"], $shipment_update_data, ShippingService::JOURNEY_TO_FACILITY); + } catch (\Exception $e) { + $shipment_data["proposal"] = $shipment["PROP"]; + $shipment_data["contact"] = $contact; + $response = $this->shipping_service->create_shipment_by_journey_type($shipment_data, $journey_type); + } + + // Dispatch shipment in shipping service + $shipmentId = $response["shipmentId"]; + $dispatch_details = $this->shipping_service->dispatch_shipment($shipmentId, false); + + $awb_pieces = array_map( + function($package, $index) {return array("piecenumber" => $index+1, "licenseplate" => $package["tracking_number"]);}, + $dispatch_details["packages"], + array_keys($dispatch_details["packages"]) + ); + + return array( + "awb" => $dispatch_details["tracking_number"], + "label" => $dispatch_details["air_waybill"], + "pieces" => $awb_pieces + ); + } + - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipping id specified'); + function _create_awb() + { + global $dhl_service, $dhl_service_eu, $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde, $use_shipping_service_incoming_shipments; - if (!$this->has_arg('DECLAREDVALUE')) $this->_error('No delcared value specified'); - if (!$this->has_arg('DESCRIPTION')) $this->_error('No description specified'); - if (!$this->has_arg('DEWARS')) $this->_error('No dewars specified'); - if (!is_array($this->arg('DEWARS'))) $this->_error('No dewars specified'); - - $ship = $this->db->pq("SELECT s.shippingid,s.sendinglabcontactid,s.returnlabcontactid, s.deliveryagent_agentcode, s.deliveryagent_flightcode, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.deliveryagent_pickupconfirmation, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.shippingname + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); + + if (!$this->has_arg('DECLAREDVALUE')) + $this->_error('No declared value specified'); + if (!$this->has_arg('DESCRIPTION')) + $this->_error('No description specified'); + if (!$this->has_arg('DEWARS')) + $this->_error('No dewars specified'); + if (!is_array($this->arg('DEWARS'))) + $this->_error('No dewars specified'); + + $ship = $this->db->pq("SELECT s.shippingid,s.sendinglabcontactid,s.returnlabcontactid, s.deliveryagent_agentcode, s.deliveryagent_flightcode, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.deliveryagent_pickupconfirmation, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.shippingname FROM shipping s INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - if (!sizeof($ship)) $this->_error('No such shipment'); - $ship = $ship[0]; - - $ids = range(2,sizeof($this->arg('DEWARS'))+1); - $args = array_merge(array($ship['SHIPPINGID']), $this->arg('DEWARS')); - $dewars = $this->db->pq("SELECT d.dewarid, d.weight, IF(d.facilitycode, d.facilitycode, d.code) as name + WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); + if (!sizeof($ship)) + $this->_error('No such shipment'); + $ship = $ship[0]; + + $ids = range(2, sizeof($this->arg('DEWARS')) + 1); + $args = array_merge(array($ship['SHIPPINGID']), $this->arg('DEWARS')); + $dewars = $this->db->pq("SELECT d.dewarid, d.weight, IF(d.facilitycode, d.facilitycode, d.code) as name FROM dewar d - WHERE d.shippingid=:1 AND d.dewarid IN (:".implode(',:', $ids).")", $args); + WHERE d.shippingid=:1 AND d.dewarid IN (:" . implode(',:', $ids) . ")", $args); - $terms = $this->db->pq("SELECT cta.couriertermsacceptedid + $terms = $this->db->pq("SELECT cta.couriertermsacceptedid FROM couriertermsaccepted cta INNER JOIN person p ON p.personid = cta.personid WHERE cta.proposalid=:1 AND cta.shippingid=:2", array($this->proposalid, $ship['SHIPPINGID'])); - $terms = sizeof($terms) ? true : false; + $terms = sizeof($terms) ? true : false; - $cont = $this->db->pq("SELECT p.givenname, p.familyname, p.phonenumber, p.emailaddress, l.name, l.address, l.city, l.country, l.postcode + $cont = $this->db->pq("SELECT p.givenname, p.familyname, p.phonenumber, p.emailaddress, l.name, l.address, l.city, l.country, l.postcode FROM labcontact c INNER JOIN person p ON p.personid = c.personid INNER JOIN laboratory l ON l.laboratoryid = p.laboratoryid WHERE c.labcontactid=:1 and c.proposalid=:2", array($this->has_arg('RETURN') ? $ship['RETURNLABCONTACTID'] : $ship['SENDINGLABCONTACTID'], $this->proposalid)); - if (!sizeof($cont)) $this->_error('No such lab contact'); - $cont = $cont[0]; - - if (in_array($cont['COUNTRY'], $facility_courier_countries) && $terms) { - $accno = $dhl_acc; - $payee = 'R'; - $product = $dhl_service; - } else if (in_array($cont['COUNTRY'], $facility_courier_countries_nde) && $terms) { - $accno = $dhl_acc_import; - $payee = 'R'; - $product = $dhl_service_eu; - } else { - $accno = $ship['DELIVERYAGENT_AGENTCODE']; - $payee = 'S'; - - if (!$this->has_arg('PRODUCTCODE')) $this->_error('No product code specified'); - $product = $this->arg('PRODUCTCODE'); - } - // Catch programmatic use of awb - suspend international shipments - // facility_courier_countries is an array containing United Kingdom - if (!in_array($cont['COUNTRY'], $facility_courier_countries)) { - $this->_error('International shipment bookings currently suspended. Please see MX manual for instructions'); - } - if ($this->has_arg('RETURN')) { - $accno = $ship['DELIVERYAGENT_AGENTCODE']; - $payee = 'R'; - } + if (!sizeof($cont)) + $this->_error('No such lab contact'); + $cont = $cont[0]; + + if (in_array($cont['COUNTRY'], $facility_courier_countries) && $terms) { + $accno = $dhl_acc; + $payee = 'R'; + $product = $dhl_service; + } else if (in_array($cont['COUNTRY'], $facility_courier_countries_nde) && $terms) { + $accno = $dhl_acc_import; + $payee = 'R'; + $product = $dhl_service_eu; + } else { + $accno = $ship['DELIVERYAGENT_AGENTCODE']; + $payee = 'S'; + + if (!$this->has_arg('PRODUCTCODE')) + $this->_error('No product code specified'); + $product = $this->arg('PRODUCTCODE'); + } + // Catch programmatic use of awb - suspend international shipments + // facility_courier_countries is an array containing United Kingdom + if (!in_array($cont['COUNTRY'], $facility_courier_countries)) { + $this->_error('International shipment bookings currently suspended. Please see MX manual for instructions'); + } + if ($this->has_arg('RETURN')) { + $accno = $ship['DELIVERYAGENT_AGENTCODE']; + $payee = 'R'; + } - $user = array( - 'company' => $cont['NAME'], - 'address' => $cont['ADDRESS'], - 'city' => $cont['CITY'], - 'postcode' => $cont['POSTCODE'], - 'country' => $cont['COUNTRY'], - 'name' => $cont['GIVENNAME'].' '.$cont['FAMILYNAME'], - 'phone' => $cont['PHONENUMBER'], - 'email' => $cont['EMAILADDRESS'], - ); + $user = array( + 'company' => $cont['NAME'], + 'address' => $cont['ADDRESS'], + 'city' => $cont['CITY'], + 'postcode' => $cont['POSTCODE'], + 'country' => $cont['COUNTRY'], + 'name' => $cont['GIVENNAME'] . ' ' . $cont['FAMILYNAME'], + 'phone' => $cont['PHONENUMBER'], + 'email' => $cont['EMAILADDRESS'], + ); - global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_contact, $facility_phone, $facility_email; - $facility = array( - 'company' => $facility_company, - 'address' => $facility_address, - 'city' => $facility_city, - 'postcode' => $facility_postcode, - 'country' => $facility_country, - 'name' => $facility_contact, - 'phone' => $facility_phone, - 'email' => $facility_email, - ); + global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_contact, $facility_phone, $facility_email; + $facility = array( + 'company' => $facility_company, + 'address' => $facility_address, + 'city' => $facility_city, + 'postcode' => $facility_postcode, + 'country' => $facility_country, + 'name' => $facility_contact, + 'phone' => $facility_phone, + 'email' => $facility_email, + ); - $pieces = array(); - $totalweight = 0; - $names = array(); - foreach ($dewars as $d) { - array_push($pieces, array( - 'weight' => $d['WEIGHT'], - 'width' => 40, - 'height' => 60, - 'depth' => 40, - )); - $totalweight += $d['WEIGHT']; - array_push($names, $d['NAME']); - } + $pieces = array(); + $totalweight = 0; + $names = array(); + foreach ($dewars as $d) { + array_push($pieces, array( + 'weight' => $d['WEIGHT'], + 'width' => 40, + 'height' => 60, + 'depth' => 40, + )); + $totalweight += $d['WEIGHT']; + array_push($names, $d['NAME']); + } - $emails = array($cont['EMAILADDRESS']); - global $shipbooked_email; - array_push($emails, str_replace(',', ';', $shipbooked_email)); + $emails = array($cont['EMAILADDRESS']); + global $shipbooked_email; + array_push($emails, str_replace(',', ';', $shipbooked_email)); - $awb = null; - if (!$ship['DELIVERYAGENT_FLIGHTCODE']) { - try { + $awb = null; + if (!$ship['DELIVERYAGENT_FLIGHTCODE']) { + try { + if (Utils::getValueOrDefault($use_shipping_service_incoming_shipments) && $accno === $dhl_acc) { + $journey_type = $this->has_arg('RETURN') ? ShippingService::JOURNEY_FROM_FACILITY : ShippingService::JOURNEY_TO_FACILITY; + $awb = $this->_book_shipment_in_shipping_service($user, $ship, $dewars, $journey_type); + } else { + error_log("Not using shipping service for: {$ship['SHIPPINGID']}"); $awb = $this->dhl->create_awb(array( 'payee' => $payee, 'accountnumber' => $accno, + 'shipperid' => $ship['PROP'], 'service' => $product, 'date' => $ship['DELIVERYAGENT_SHIPPINGDATE'], 'declaredvalue' => $this->arg('DECLAREDVALUE'), @@ -2280,345 +2866,380 @@ function _create_awb() { 'pieces' => $pieces, 'notification' => implode(';', $emails), - 'message' => $facility_company.': Shipment booked from ISPyB for '.$ship['PROP'].' '.$ship['SHIPPINGNAME'].' containing '.implode(',', $names) + 'message' => $facility_company . ': Shipment booked from ISPyB for ' . $ship['PROP'] . ' ' . $ship['SHIPPINGNAME'] . ' containing ' . implode(',', $names) )); + } - $this->db->pq("UPDATE shipping + $this->db->pq("UPDATE shipping SET deliveryagent_flightcode=:1, deliveryagent_flightcodetimestamp=CURRENT_TIMESTAMP, deliveryagent_label=:2, deliveryagent_productcode=:3, deliveryagent_flightcodepersonid=:4, shippingstatus='awb created', deliveryagent_agentname='DHL' - WHERE shippingid=:5", array($awb['awb'], $awb['label'], $product, $this->user->personid, $ship['SHIPPINGID'])); + WHERE shippingid=:5", array($awb['awb'], $awb['label'], $product, $this->user->personId, $ship['SHIPPINGID'])); - $tno = $this->has_arg('RETURN') ? 'trackingnumberfromsynchrotron' : 'trackingnumbertosynchrotron'; - foreach ($dewars as $i => $d) { - if ($i >= sizeof($awb['pieces'])) continue; - - $p = $awb['pieces'][$i]; - $this->db->pq("UPDATE dewar SET $tno=:1, deliveryAgent_barcode=:2, dewarstatus='awb created' WHERE dewarid=:3", array($awb['awb'], $p['licenseplate'], $d['DEWARID'])); - - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,arrivaldate) - VALUES (s_dewartransporthistory.nextval,:1,'awb created',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", - array($d['DEWARID'])); - } + $tno = $this->has_arg('RETURN') ? 'trackingnumberfromsynchrotron' : 'trackingnumbertosynchrotron'; + foreach ($dewars as $i => $d) { + if ($i >= sizeof($awb['pieces'])) + continue; - $ship['DELIVERYAGENT_FLIGHTCODE'] = $awb['awb']; + $p = $awb['pieces'][$i]; + $this->db->pq("UPDATE dewar SET $tno=:1, deliveryAgent_barcode=:2, dewarstatus='awb created', storagelocation='off-site' WHERE dewarid=:3", array($awb['awb'], $p['licenseplate'], $d['DEWARID'])); - } catch (\Exception $e) { - $this->_error($e->getMessage()); + $this->db->pq( + "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) + VALUES (s_dewartransporthistory.nextval,:1,'awb created','off-site',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", + array($d['DEWARID']) + ); } - } - $pickup = null; - if (!$ship['DELIVERYAGENT_PICKUPCONFIRMATION']) { - $pickup = $this->_do_request_pickup(array( - 'shippingid' => $ship['SHIPPINGID'], - 'accountnumber' => $accno, - 'requestor' => $this->has_arg('RETURN') ? $facility : $user, - 'packagelocation' => $ship['PHYSICALLOCATION'], + $ship['DELIVERYAGENT_FLIGHTCODE'] = $awb['awb']; + } catch (\Exception $e) { + $this->_error($e->getMessage()); + } + } + $pickup = null; + if (!$ship['DELIVERYAGENT_PICKUPCONFIRMATION']) { - 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'], - 'readyby' => $ship['READYBYTIME'], - 'closetime' => $ship['CLOSETIME'], + $pickup = $this->_do_request_pickup(array( + 'shippingid' => $ship['SHIPPINGID'], + 'accountnumber' => $accno, + 'requestor' => $this->has_arg('RETURN') ? $facility : $user, + 'packagelocation' => $ship['PHYSICALLOCATION'], - 'dewars' => $dewars, - 'weight' => $totalweight, + 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'], + 'readyby' => $ship['READYBYTIME'], + 'closetime' => $ship['CLOSETIME'], - 'awbnumber' => $ship['DELIVERYAGENT_FLIGHTCODE'], - )); - } + 'dewars' => $dewars, + 'weight' => $totalweight, - $this->_output(array('AWB' => $awb ? 1 : 0, 'PICKUP' => $pickup ? 1 : 0)); + 'awbnumber' => $ship['DELIVERYAGENT_FLIGHTCODE'], + )); } + $this->_output(array('AWB' => $awb ? 1 : 0, 'PICKUP' => $pickup ? 1 : 0)); + } + function _rebook_pickup() + { + global $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde; + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - function _rebook_pickup() { - global $dhl_acc, $dhl_acc_import, $facility_courier_countries, $facility_courier_countries_nde; - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - - $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.sendinglabcontactid, s.returnlabcontactid, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, s.deliveryagent_flightcode, s.deliveryagent_agentcode + $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.sendinglabcontactid, s.returnlabcontactid, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, TO_CHAR(s.closetime, 'HH24:MI') as closetime, s.physicallocation, s.deliveryagent_flightcode, s.deliveryagent_agentcode FROM shipping s INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - if (!sizeof($ship)) $this->_error('No such shipment'); - $ship = $ship[0]; + WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); + if (!sizeof($ship)) + $this->_error('No such shipment'); + $ship = $ship[0]; - if (!$ship['DELIVERYAGENT_FLIGHTCODE']) $this->_error('That shipment does not have an airway bill'); + if (!$ship['DELIVERYAGENT_FLIGHTCODE']) + $this->_error('That shipment does not have an airway bill'); - $cont = $this->db->pq("SELECT p.givenname, p.familyname, p.phonenumber, p.emailaddress, l.name, l.address, l.city, l.country, l.postcode + $cont = $this->db->pq("SELECT p.givenname, p.familyname, p.phonenumber, p.emailaddress, l.name, l.address, l.city, l.country, l.postcode FROM labcontact c INNER JOIN person p ON p.personid = c.personid INNER JOIN laboratory l ON l.laboratoryid = p.laboratoryid WHERE c.labcontactid=:1 and c.proposalid=:2", array($this->has_arg('RETURN') ? $ship['RETURNLABCONTACTID'] : $ship['SENDINGLABCONTACTID'], $this->proposalid)); - if (!sizeof($cont)) $this->_error('No such lab contact'); - $cont = $cont[0]; + if (!sizeof($cont)) + $this->_error('No such lab contact'); + $cont = $cont[0]; - $terms = $this->db->pq("SELECT cta.couriertermsacceptedid + $terms = $this->db->pq("SELECT cta.couriertermsacceptedid FROM couriertermsaccepted cta INNER JOIN person p ON p.personid = cta.personid WHERE cta.proposalid=:1 AND cta.shippingid=:2", array($this->proposalid, $ship['SHIPPINGID'])); - $terms = sizeof($terms) ? true : false; - - if (in_array($cont['COUNTRY'], $facility_courier_countries) && $terms) { - $accno = $dhl_acc; - } else if (in_array($cont['COUNTRY'], $facility_courier_countries_nde) && $terms) { - $accno = $dhl_acc_import; - } else { - $accno = $ship['DELIVERYAGENT_AGENTCODE']; - } - + $terms = sizeof($terms) ? true : false; + + if (in_array($cont['COUNTRY'], $facility_courier_countries) && $terms) { + $accno = $dhl_acc; + } else if (in_array($cont['COUNTRY'], $facility_courier_countries_nde) && $terms) { + $accno = $dhl_acc_import; + } else { + $accno = $ship['DELIVERYAGENT_AGENTCODE']; + } - $user = array( - 'company' => $cont['NAME'], - 'address' => $cont['ADDRESS'], - 'city' => $cont['CITY'], - 'postcode' => $cont['POSTCODE'], - 'country' => $cont['COUNTRY'], - 'name' => $cont['GIVENNAME'].' '.$cont['FAMILYNAME'], - 'phone' => $cont['PHONENUMBER'], - 'email' => $cont['EMAILADDRESS'], - ); + $user = array( + 'company' => $cont['NAME'], + 'address' => $cont['ADDRESS'], + 'city' => $cont['CITY'], + 'postcode' => $cont['POSTCODE'], + 'country' => $cont['COUNTRY'], + 'name' => $cont['GIVENNAME'] . ' ' . $cont['FAMILYNAME'], + 'phone' => $cont['PHONENUMBER'], + 'email' => $cont['EMAILADDRESS'], + ); - global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_contact, $facility_phone, $facility_email; - $facility = array( - 'company' => $facility_company, - 'address' => $facility_address, - 'city' => $facility_city, - 'postcode' => $facility_postcode, - 'country' => $facility_country, - 'name' => $facility_contact, - 'phone' => $facility_phone, - 'email' => $facility_email, - ); + global $facility_company, $facility_address, $facility_city, $facility_postcode, $facility_country, $facility_contact, $facility_phone, $facility_email; + $facility = array( + 'company' => $facility_company, + 'address' => $facility_address, + 'city' => $facility_city, + 'postcode' => $facility_postcode, + 'country' => $facility_country, + 'name' => $facility_contact, + 'phone' => $facility_phone, + 'email' => $facility_email, + ); - $dewars = $this->db->pq("SELECT d.dewarid, d.weight + $dewars = $this->db->pq("SELECT d.dewarid, d.weight FROM dewar d WHERE d.shippingid=:1 AND d.deliveryagent_barcode IS NOT NULL", array($this->arg('sid'))); - if (!sizeof($dewars)) $this->_error('No dewars selected to ship'); + if (!sizeof($dewars)) + $this->_error('No dewars selected to ship'); - $totalweight = 0; - foreach ($dewars as $d) { - $totalweight += $d['WEIGHT']; - } + $totalweight = 0; + foreach ($dewars as $d) { + $totalweight += $d['WEIGHT']; + } - $pickup = $this->_do_request_pickup(array( - 'shippingid' => $ship['SHIPPINGID'], - 'accountnumber' => $accno, - 'requestor' => $this->has_arg('RETURN') ? $facility : $user, - 'packagelocation' => $ship['PHYSICALLOCATION'], + $pickup = $this->_do_request_pickup(array( + 'shippingid' => $ship['SHIPPINGID'], + 'accountnumber' => $accno, + 'requestor' => $this->has_arg('RETURN') ? $facility : $user, + 'packagelocation' => $ship['PHYSICALLOCATION'], - 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'], - 'readyby' => $ship['READYBYTIME'], - 'closetime' => $ship['CLOSETIME'], + 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'], + 'readyby' => $ship['READYBYTIME'], + 'closetime' => $ship['CLOSETIME'], - 'dewars' => $dewars, - 'weight' => $totalweight, + 'dewars' => $dewars, + 'weight' => $totalweight, - 'awbnumber' => $ship['DELIVERYAGENT_FLIGHTCODE'], - )); + 'awbnumber' => $ship['DELIVERYAGENT_FLIGHTCODE'], + )); - $this->_output(array('PICKUP' => $pickup ? 1 : 0)); - } + $this->_output(array('PICKUP' => $pickup ? 1 : 0)); + } - function _do_request_pickup($options) { - $pickup = null; - try { - $pickup = $this->dhl->request_pickup(array( - 'accountnumber' => $options['accountnumber'], - 'requestor' => $options['requestor'], - 'packagelocation' => $options['packagelocation'], + function _do_request_pickup($options) + { + $pickup = null; + try { + $pickup = $this->dhl->request_pickup(array( + 'accountnumber' => $options['accountnumber'], + 'requestor' => $options['requestor'], + 'packagelocation' => $options['packagelocation'], - 'pickupdate' => $options['pickupdate'], - 'readyby' => $options['readyby'], - 'closetime' => $options['closetime'], + 'pickupdate' => $options['pickupdate'], + 'readyby' => $options['readyby'], + 'closetime' => $options['closetime'], - 'pieces' => sizeof($options['dewars']), - 'weight' => $options['weight'], + 'pieces' => sizeof($options['dewars']), + 'weight' => $options['weight'], - 'awbnumber' => $options['awbnumber'], - )); + 'awbnumber' => $options['awbnumber'], + )); - $this->db->pq("UPDATE shipping + $this->db->pq("UPDATE shipping SET deliveryagent_pickupconfirmation=:1, deliveryagent_pickupconfirmationtimestamp=CURRENT_TIMESTAMP, deliveryAgent_readybytime=TO_DATE(:2, 'HH24:MI'), deliveryAgent_callintime=TO_DATE(:3, 'HH24:MI'), shippingstatus='pickup booked' WHERE shippingid=:4", array($pickup['confirmationnumber'], $pickup['readybytime'], $pickup['callintime'], $options['shippingid'])); - foreach ($options['dewars'] as $i => $d) { - $this->db->pq("UPDATE dewar SET dewarstatus='pickup booked' WHERE dewarid=:1", array($d['DEWARID'])); - $this->db->pq("INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,arrivaldate) - VALUES (s_dewartransporthistory.nextval,:1,'pickup booked',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", - array($d['DEWARID'])); - } - - } catch (\Exception $e) { - $this->_error($e->getMessage()); - } - - return $pickup; + foreach ($options['dewars'] as $i => $d) { + $this->db->pq("UPDATE dewar SET dewarstatus='pickup booked', storagelocation='off-site' WHERE dewarid=:1", array($d['DEWARID'])); + $this->db->pq( + "INSERT INTO dewartransporthistory (dewartransporthistoryid,dewarid,dewarstatus,storagelocation,arrivaldate) + VALUES (s_dewartransporthistory.nextval,:1,'pickup booked','off-site',CURRENT_TIMESTAMP) RETURNING dewartransporthistoryid INTO :id", + array($d['DEWARID']) + ); + } + } catch (\Exception $e) { + $this->_error($e->getMessage()); } + return $pickup; + } - function _quote_awb() { - global $facility_city, $facility_postcode, $facility_country, $facility_courier_countries; - - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - if (!$this->has_arg('sid')) $this->_error('No shipping id specified'); - if (!$this->has_arg('DECLAREDVALUE')) $this->_error('No delcared value specified'); - if (!$this->has_arg('DEWARS')) $this->_error('No dewars specified'); - if (!is_array($this->arg('DEWARS'))) $this->_error('No dewars specified'); - - $ship = $this->db->pq("SELECT s.shippingid,s.sendinglabcontactid,s.returnlabcontactid, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, TO_CHAR(s.readybytime, 'HH24:MI') as readybytime - FROM shipping s - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - if (!sizeof($ship)) $this->_error('No such shipment'); - $ship = $ship[0]; + function _quote_awb() + { + global $facility_city, $facility_postcode, $facility_country, $facility_courier_countries; + + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + if (!$this->has_arg('sid')) + $this->_error('No shipping id specified'); + + if (!$this->has_arg('DECLAREDVALUE')) + $this->_error('No declared value specified'); + if (!$this->has_arg('DEWARS')) + $this->_error('No dewars specified'); + if (!is_array($this->arg('DEWARS'))) + $this->_error('No dewars specified'); + + $ship = $this->db->pq( + "SELECT + s.shippingid, + s.sendinglabcontactid, + s.returnlabcontactid, + TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, + TO_CHAR(s.readybytime, 'HH24:MI') as readybytime, + s.deliveryagent_agentcode + FROM shipping s + WHERE s.proposalid = :1 AND s.shippingid = :2", + array($this->proposalid, $this->arg('sid')) + ); + if (!sizeof($ship)) $this->_error('No such shipment'); + $ship = $ship[0]; - $ids = range(2,sizeof($this->arg('DEWARS'))+1); - $args = array_merge(array($ship['SHIPPINGID']), $this->arg('DEWARS')); - $dewars = $this->db->pq("SELECT d.dewarid, d.weight + $ids = range(2, sizeof($this->arg('DEWARS')) + 1); + $args = array_merge(array($ship['SHIPPINGID']), $this->arg('DEWARS')); + $dewars = $this->db->pq("SELECT d.dewarid, d.weight FROM dewar d - WHERE d.shippingid=:1 AND d.dewarid IN (:".implode(',:', $ids).")", $args); + WHERE d.shippingid=:1 AND d.dewarid IN (:" . implode(',:', $ids) . ")", $args); - $cont = $this->db->pq("SELECT l.city, l.country, l.postcode + $cont = $this->db->pq("SELECT l.city, l.country, l.postcode FROM labcontact c INNER JOIN person p ON p.personid = c.personid INNER JOIN laboratory l ON l.laboratoryid = p.laboratoryid WHERE c.labcontactid=:1 and c.proposalid=:2", array($this->has_arg('RETURN') ? $ship['RETURNLABCONTACTID'] : $ship['SENDINGLABCONTACTID'], $this->proposalid)); - if (!sizeof($cont)) $this->_error('No such lab contact'); - $cont = $cont[0]; + if (!sizeof($cont)) + $this->_error('No such lab contact'); + $cont = $cont[0]; - $user = array( - 'city' => $cont['CITY'], - 'postcode' => $cont['POSTCODE'], - 'country' => $cont['COUNTRY'], - ); + $user = array( + 'city' => $cont['CITY'], + 'postcode' => $cont['POSTCODE'], + 'country' => $cont['COUNTRY'], + ); - // Catch programmatic use of awb - suspend international shipments - // facility_courier_countries is an array containing United Kingdom - if (!in_array($cont['COUNTRY'], $facility_courier_countries)) { - $this->_error('International shipment bookings currently suspended. Please see MX manual for instructions'); - } + // Catch programmatic use of awb - suspend international shipments + // facility_courier_countries is an array containing United Kingdom + if (!in_array($cont['COUNTRY'], $facility_courier_countries)) { + $this->_error('International shipment bookings currently suspended. Please see MX manual for instructions'); + } - $facility = array( - 'city' => $facility_city, - 'postcode' => $facility_postcode, - 'country' => $facility_country, - ); + $facility = array( + 'city' => $facility_city, + 'postcode' => $facility_postcode, + 'country' => $facility_country, + ); - $pieces = array(); - foreach ($dewars as $d) { - array_push($pieces, array( - 'weight' => $d['WEIGHT'], - 'width' => 40, - 'height' => 60, - 'depth' => 40, - )); - } + $pieces = array(); + foreach ($dewars as $d) { + array_push($pieces, array( + 'weight' => $d['WEIGHT'], + 'width' => 40, + 'height' => 60, + 'depth' => 40, + )); + } - try { - $products = $this->dhl->get_quote(array( - 'date' => $ship['DELIVERYAGENT_SHIPPINGDATE'], - 'declaredvalue' => $this->arg('DECLAREDVALUE'), - 'readyby' => $ship['READYBYTIME'], + try { + $products = $this->dhl->get_quote(array( + 'date' => $ship['DELIVERYAGENT_SHIPPINGDATE'], + 'declaredvalue' => $this->arg('DECLAREDVALUE'), + 'readyby' => $ship['READYBYTIME'], - 'sender' => $this->has_arg('RETURN') ? $facility : $user, - 'receiver' => $this->has_arg('RETURN') ? $user : $facility, + 'payment_account_number' => $ship['DELIVERYAGENT_AGENTCODE'], - 'pieces' => $pieces, - )); + 'sender' => $this->has_arg('RETURN') ? $facility : $user, + 'receiver' => $this->has_arg('RETURN') ? $user : $facility, - $this->_output($products); + 'pieces' => $pieces, + )); - } catch (\Exception $e) { - $this->_error($e->getMessage()); - } + $this->_output($products); + } catch (\Exception $e) { + $this->_error($e->getMessage()); } + } - function _cancel_pickup() { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); + function _cancel_pickup() + { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); - $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.sendinglabcontactid, s.returnlabcontactid + $ship = $this->db->pq("SELECT s.shippingid, s.deliveryagent_pickupconfirmation, TO_CHAR(s.deliveryagent_shippingdate, 'YYYY-MM-DD') as deliveryagent_shippingdate, s.sendinglabcontactid, s.returnlabcontactid FROM shipping s INNER JOIN proposal p ON p.proposalid = s.proposalid - WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid,$this->arg('sid'))); - if (!sizeof($ship)) $this->_error('No such shipment'); - $ship = $ship[0]; + WHERE s.proposalid = :1 AND s.shippingid = :2", array($this->proposalid, $this->arg('sid'))); + if (!sizeof($ship)) + $this->_error('No such shipment'); + $ship = $ship[0]; - $cont = $this->db->pq("SELECT l.country + $cont = $this->db->pq("SELECT l.country FROM labcontact c INNER JOIN person p ON p.personid = c.personid INNER JOIN laboratory l ON l.laboratoryid = p.laboratoryid WHERE c.labcontactid=:1 and c.proposalid=:2", array($this->has_arg('RETURN') ? $ship['RETURNLABCONTACTID'] : $ship['SENDINGLABCONTACTID'], $this->proposalid)); - if (!sizeof($cont)) $this->_error('No such lab contact'); - $cont = $cont[0]; + if (!sizeof($cont)) + $this->_error('No such lab contact'); + $cont = $cont[0]; - $dewars = $this->db->pq("SELECT d.dewarid + $dewars = $this->db->pq("SELECT d.dewarid, d.storagelocation FROM dewar d WHERE d.shippingid=:1 AND d.deliveryagent_barcode IS NOT NULL", array($this->arg('sid'))); - $person = $this->user->givenname.' '.$this->user->familyname; + $person = $this->user->givenName . ' ' . $this->user->familyName; - $cancel = null; - try { - $cancel = $this->dhl->cancel_pickup(array( - 'name' => $person, - 'country' => $cont['COUNTRY'], - 'confirmationnumber' => $ship['DELIVERYAGENT_PICKUPCONFIRMATION'], - 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'] - )); + $cancel = null; + try { + $cancel = $this->dhl->cancel_pickup(array( + 'name' => $person, + 'country' => $cont['COUNTRY'], + 'confirmationnumber' => $ship['DELIVERYAGENT_PICKUPCONFIRMATION'], + 'pickupdate' => $ship['DELIVERYAGENT_SHIPPINGDATE'] + )); - $this->db->pq("UPDATE shipping + $this->db->pq("UPDATE shipping SET deliveryagent_pickupconfirmation=NULL, deliveryagent_pickupconfirmationtimestamp=NULL, deliveryAgent_readybytime=NULL, deliveryAgent_callintime=NULL, shippingstatus='pickup cancelled' WHERE shippingid=:1", array($ship['SHIPPINGID'])); - foreach ($dewars as $i => $d) { - $this->db->pq("UPDATE dewar SET dewarstatus='pickup cancelled' WHERE dewarid=:1", array($d['DEWARID'])); - $this->db->pq("INSERT INTO dewartransporthistory (dewarid,dewarstatus,arrivaldate) - VALUES (:1,'pickup cancelled',CURRENT_TIMESTAMP)", - array($d['DEWARID'])); - } - - } catch (\Exception $e) { - $this->_error($e->getMessage()); + foreach ($dewars as $i => $d) { + $this->db->pq("UPDATE dewar SET dewarstatus='pickup cancelled' WHERE dewarid=:1", array($d['DEWARID'])); + $loc = Utils::getValueOrDefault($d['STORAGELOCATION'], ''); + $this->db->pq( + "INSERT INTO dewartransporthistory (dewarid,dewarstatus,storagelocation,arrivaldate) + VALUES (:1,'pickup cancelled',:2,CURRENT_TIMESTAMP)", + array($d['DEWARID'], $loc) + ); } - - $this->_output(array('CANCEL' => $cancel ? 1 : 0)); + } catch (\Exception $e) { + $this->_error($e->getMessage()); } + $this->_output(array('CANCEL' => $cancel ? 1 : 0)); + } - function _get_countries() { - $data = array(); - foreach ($this->dhl->get_countries() as $c) { - array_push($data, array('TITLE' => $c)); - } - $this->_output($data); + function _get_countries() + { + $data = array(); + foreach ($this->dhl->get_countries() as $c) { + array_push($data, array('TITLE' => $c)); } - /** - * Controller method for notify container endpoint. - * This is called from an acquisition system when new data is available. - * - * The calling application must be in the auto whitelist group and include the BARCODE in the URL - * On success, the container status is updated to "notify_email". - * If the container status is anything other than "notify_email", send email if preconditions met. - * Preconditions are that the container has an owner with an email address and the container has entries in container history. - * Returns 200 on success or if status is notify_email. Response body is {'EMAIL_SENT' => 1 or 0} depending on if email was sent. - * Returns 404 if no container found, 412 if preconditions not met - */ - function _notify_container() { - global $auto; - - if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) $this->_error('You do not have access to that resource'); - if (!$this->has_arg('BARCODE')) $this->_error('No container specified'); + $this->_output($data); + } - $cont = $this->db->pq("SELECT c.containerid, pe.emailaddress, pe.givenname, pe.familyname, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.code, sh.shippingname + /** + * Controller method for notify container endpoint. + * This is called from an acquisition system when new data is available. + * + * The calling application must be in the auto whitelist group and include the BARCODE in the URL + * On success, the container status is updated to "notify_email". + * If the container status is anything other than "notify_email", send email if preconditions met. + * Preconditions are that the container has an owner with an email address and the container has entries in container history. + * Returns 200 on success or if status is notify_email. Response body is {'EMAIL_SENT' => 1 or 0} depending on if email was sent. + * Returns 404 if no container found, 412 if preconditions not met + */ + function _notify_container() + { + global $auto; + + if (!(in_array($_SERVER["REMOTE_ADDR"], $auto))) + $this->_error('You do not have access to that resource'); + if (!$this->has_arg('BARCODE')) + $this->_error('No container specified'); + + $cont = $this->db->pq("SELECT c.containerid, pe.emailaddress, pe.givenname, pe.familyname, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, CONCAT(p.proposalcode, p.proposalnumber) as prop, c.code, sh.shippingname FROM container c INNER JOIN dewar d ON d.dewarid = c.dewarid INNER JOIN shipping sh ON sh.shippingid = d.shippingid @@ -2627,32 +3248,35 @@ function _notify_container() { INNER JOIN proposal p ON p.proposalid = s.proposalid WHERE c.barcode=:1", array($this->arg('BARCODE'))); - if (!sizeof($cont)) $this->_error('Container not found', 404); + if (!sizeof($cont)) + $this->_error('Container not found', 404); - $cont = $cont[0]; + $cont = $cont[0]; - if (!$cont['EMAILADDRESS']) $this->_error('Precondition failed, no email address for this container owner', 412); + if (!$cont['EMAILADDRESS']) + $this->_error('Precondition failed, no email address for this container owner', 412); - $hist = $this->db->pq("SELECT h.containerhistoryid, h.status, h.bltimestamp + $hist = $this->db->pq("SELECT h.containerhistoryid, h.status, h.bltimestamp FROM containerhistory h WHERE h.containerid=:1 ORDER BY h.containerhistoryid DESC LIMIT 1", array($cont['CONTAINERID'])); - if (!sizeof($hist)) $this->_error('Precondition failed, no history for this container', 412); + if (!sizeof($hist)) + $this->_error('Precondition failed, no history for this container', 412); - // Case insensitive check. If we have already sent an email, return. - if (strcasecmp($hist[0]['STATUS'], 'notify_email') == 0) { - $this->_output(array('EMAIL_SENT' => 0)); - } else { - $email = new Email('data-new', '*** New data available for your container ***'); - $email->data = $cont; - $email->send($cont['EMAILADDRESS']); + // Case insensitive check. If we have already sent an email, return. + if (strcasecmp($hist[0]['STATUS'], 'notify_email') == 0) { + $this->_output(array('EMAIL_SENT' => 0)); + } else { + $email = new Email('data-new', '*** New data available for your container ***'); + $email->data = $cont; + $email->send($cont['EMAILADDRESS']); - $this->db->pq("INSERT INTO containerhistory (status,containerid) VALUES (:1, :2)", array('notify_email', $cont['CONTAINERID'])); + $this->db->pq("INSERT INTO containerhistory (status,containerid) VALUES (:1, :2)", array('notify_email', $cont['CONTAINERID'])); - // Everything OK, send result - $this->_output(array('EMAIL_SENT' => 1)); - } + // Everything OK, send result + $this->_output(array('EMAIL_SENT' => 1)); } } +} diff --git a/api/src/Page/Stats.php b/api/src/Page/Stats.php index 77952288e..652abfd1d 100644 --- a/api/src/Page/Stats.php +++ b/api/src/Page/Stats.php @@ -27,7 +27,7 @@ class Stats extends Page # Whos online list function _online_users() { - $rows = $this->db->pq("SELECT a.username, a.comments, TO_CHAR(a.datetime, 'DD-MM-YYYY HH24:MI:SS') as time, CONCAT(CONCAT(p.givenname, ' '), p.familyname) as name + $rows = $this->db->pq("SELECT a.username, a.comments, TO_CHAR(a.datetime, 'DD-MM-YYYY HH24:MI:SS') as time, CONCAT(p.givenname, ' ', p.familyname) as name FROM adminactivity a LEFT OUTER JOIN person p ON p.login = a.username WHERE TIMESTAMPDIFF('MINUTE', a.datetime, CURRENT_TIMESTAMP) < 15 ORDER BY a.datetime DESC"); @@ -37,7 +37,7 @@ function _online_users() { function _last_actions() { - $rows = $this->db->paginate("SELECT a.username, a.comments, TO_CHAR(a.datetime, 'DD-MM-YYYY HH24:MI:SS') as time, CONCAT(CONCAT(p.givenname, ' '), p.familyname) as name + $rows = $this->db->paginate("SELECT a.username, a.comments, TO_CHAR(a.datetime, 'DD-MM-YYYY HH24:MI:SS') as time, CONCAT(p.givenname, ' ', p.familyname) as name FROM adminactivity a LEFT OUTER JOIN person p ON p.login = a.username WHERE comments LIKE 'ISPyB2%' @@ -97,8 +97,10 @@ function _beamline() { ); $t = $this->has_arg('t') ? $this->arg('t') : 'dc'; - - if (array_key_exists($t, $types)) $this->$types[$t](); + if (array_key_exists($t, $types)) { + $methodToCall = $types[$t]; + $this->$methodToCall(); + } else $this->_error('No such stat type'); } diff --git a/api/src/Page/Status.php b/api/src/Page/Status.php index 04e85894e..8d17e4d9d 100644 --- a/api/src/Page/Status.php +++ b/api/src/Page/Status.php @@ -8,7 +8,7 @@ class Status extends Page { - public static $arg_list = array('bl' => '[\w-]+', + public static $arg_list = array('bl' => '[\w\-]+', 'p' => '\d+', 'st' => '\d\d-\d\d-\d\d\d\d', 'en' => '\d\d-\d\d-\d\d\d\d', @@ -22,17 +22,6 @@ class Status extends Page array('/epics/:bl/c/:c', 'get', '_get_component'), ); - /* - var $dispatch = array('pvs' => '_get_pvs', - 'log' => '_get_server_log', - 'epics' => '_get_component', - 'ep' => '_epics_pages', - ); - - var $def = 'pvs'; - #var $profile = True; - //var $debug = True; - */ # ------------------------------------------------------------------------ # Return beam / ring status pvs for a beamline @@ -80,7 +69,8 @@ function _get_pvs() { function _get_component() { if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - + $beamline = $this->arg('bl'); + if (!file_exists('tables/motors.json')) $this->_error('Couldn\'t find motors file'); $json = preg_replace("/(\s\s+|\n)/", '', file_get_contents('tables/motors.json')); $pages = json_decode($json, true); @@ -102,35 +92,33 @@ function _get_component() { # Motors if ($t == 1) { foreach ($vals as $i => $s) { - array_push($pvs, $bls[$this->arg('bl')].'-'.$p.'.'.$s); + array_push($pvs, $bls[$beamline].'-'.$p.'.'.$s); } # Toggles } else if ($t == 2) { - array_push($pvs, $bls[$this->arg('bl')].'-'.$p); + array_push($pvs, $bls[$beamline].'-'.$p); } } - $pvv = $this->pv($pvs); - + foreach ($pvp as $n => $pt) { list($pv, $t) = $pt; $output[$n] = array('t' => $t, 'val' => array()); # Motors if ($t == 1) { foreach ($vals as $i => $s) { - $p = $bls[$this->arg('bl')].'-'.$pv.'.'.$s; - $output[$n]['val'][$s] = $pvv[$p]; + $p = $bls[$beamline].'-'.$pv.'.'.$s; + $output[$n]['val'][$s] = array_key_exists($p, $pvv) ? $pvv[$p] : null; } # Toggles } else if ($t == 2) { - $p = $bls[$this->arg('bl')].'-'.$pv; - $output[$n]['val'] = $pvv[$p] == $pt[2]; + $p = $bls[$beamline].'-'.$pv; + $output[$n]['val'] = array_key_exists($p, $pvv) ? $pvv[$p] == $pt[2] : false; } } } - $this->_output($output); } @@ -167,7 +155,6 @@ function _get_server_log() { for ($line = 0, $lines = array(); $line < $num_lines && false !== ($char = fgetc($file));) { if ($char === "\n"){ if(isset($lines[$line])){ - //$lines[$line][] = $char; $lines[$line] = implode('', array_reverse($lines[$line])); $line++; } diff --git a/api/src/Page/Summary.php b/api/src/Page/Summary.php new file mode 100644 index 000000000..7b34bc8d7 --- /dev/null +++ b/api/src/Page/Summary.php @@ -0,0 +1,423 @@ + '(\w|\s|\-|\(|\))+', + 'propid' => '(.*)', + + // visit + 'com' => '(\[[^\ \]]*\],(asc|desc))', //comment + 'PROPOSALID' => '(\[[^\ \]]*\],(asc|desc))', + + 'BEAMLINENAME' => '(\[[^\ \]]*\],(asc|desc))', + // 'VISITNUMBER' => '(\[[^\ \]]*\],(asc|desc))', + + // Filter Parameters - array of parameter orders e.g. ascending, descending and the comparison value + 'sample' => '(^(\w+),(asc|desc|)+$)', //sample name + 'filetemp' => '(^(\w+),(asc|desc|)+$)', //file template + // 'dcid' => '(.*)', //data collection id + 'pp' => '(\[[^\ \]]*\],(asc|desc|))', // processing programs + 'sg' => '(\[[^\ \]]*\],(asc|desc|))', // space group + + + // Filter Parameters - array of parameter operands and orders such as greater than, equal to, between, less than, like, ascending, descending and the comparison value. + 'STARTDATE' => '(.*)', // visit start date + 'rca' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type a + 'rcb' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type b + 'rcc' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type c + 'rcal' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type alpha + 'rcbe' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type beta + 'rcga' => '(^(\d+),(.+),(asc|desc|)+$)', // refined cell type gamma + 'rlho' => '(^(\d+),(.+),(asc|desc|)+$)', // resolution limit high outer + 'rmpmi' => '(^(\d+),(.+),(asc|desc|)+$)', // rmeaswithiniplusiminus inner + 'riso' => '(^(\d+),(.+),(asc|desc|)+$)', // resioversigi2 overall + 'cci' => '(^(\d+),(.+),(asc|desc|)+$)', // ccanomalous inner + 'cco' => '(^(\d+),(.+),(asc|desc|)+$)', // ccanomalous overall + 'rfsi' => '(^(\d+),(.+),(asc|desc|)+$)', // rfreevaluestart inner + 'rfei' => '(^(\d+),(.+),(asc|desc|)+$)', // rfreevalueend inner + 'nobi' => '(^(\d+),(.+),(asc|desc|)+$)', // noofblobs inner + + + ); + + public static $dispatch = array( + array('/results', 'get', '_get_results'), + array('/proposal', 'get', '_get_proposal'), + array('/spacegroup', 'get', '_get_spacegroup'), + array('/procprogram', 'get', '_get_processingprogram'), + array('/bl', 'get', '_get_beamline') + ); + + public $string_arg_types = array( + 'pp' => ["order" =>'ppt.processingPrograms ', "where" => 'lower(ppt.processingPrograms) = lower( ? ) '], + 'sg' => ["order" =>'sgt.spaceGroup ', "where" => 'lower(sgt.spaceGroup) = lower( ? ) '], + 'BEAMLINENAME' => ["order" =>'vt.beamLineName ', "where" => 'lower(vt.beamLineName) LIKE lower( ? ) '] + ); + + + public $val_arg_types = array( + 'rca' => ["arg" => 'sf.refinedCell_a '], + 'rcb' => ["arg" => 'sf.refinedCell_b '], + 'rcc' => ["arg" => 'sf.refinedCell_c '], + 'rcal' => ["arg" => 'sf.refinedCell_alpha '], + 'rcbe' => ["arg" => 'sf.refinedCell_beta '], + 'rcga' => ["arg" => 'sf.refinedCell_gamma '], + 'rlho' => ["arg" => 'sf.resolutionLimitHighOuter '], + 'rmpmi' => ["arg" => 'sf.rMeasWithinIPlusIMinusInner '], + 'riso' => ["arg" => 'sf.resIOverSigI2Overall '], + 'cci' => ["arg" => 'sf.ccAnomalousInner '], + 'cco' => ["arg" => 'sf.ccAnomalousOverall '], + 'rfsi' => ["arg" => 'sf.rFreeValueStartInner '], + 'rfei' => ["arg" => 'sf.rFreeValueEndInner '], + 'nobi' => ["arg" => 'sf.noofblobs '] + + ); + + + private $summarydb; + + + function __construct(Slim $app, $db, $user) + { + global $summarydbconfig; + + parent::__construct($app, $db, $user); + + if ($summarydbconfig) { + $dbFactory = new DatabaseFactory(new DatabaseConnectionFactory()); + $db = $dbFactory->get("summary"); + $this->summarydb = $db; + } + + } + + private function error_on_no_summary_db(){ + global $summarydbconfig; + if (!$summarydbconfig) { + $this->_error("Not valid when summary database not configured."); + } + } + + function _get_results() { + $this->error_on_no_summary_db(); + $where = ''; + $where_arr = array(); + $order = ''; + $order_arr = array(); + $args = array(); + + $op_array = array(">", "LIKE", "=", "<"); + + if (!$this->has_arg('propid')) $this->_error('No proposal defined'); + + // $propid_array = explode(',', $this->arg('propid')); + $propid_args = preg_split('/[,]+(?![^\[]*\])/', urldecode($this->arg('propid'))); + $propid_array = explode(',', implode(str_replace(array('[',']'),'', $propid_args))); + array_push($order_arr, 'sf.autoProcIntegrationId DESC'); + + + if (!$this->staff) { + $person_id = $this->user->personId; + + $where_propid_personid_array = array(); + + + // # check user can see selected proposals and get all available visits if so + foreach ($propid_array as $value) { + array_push($where_propid_personid_array, '(sf.personid = ?'); + array_push($where_propid_personid_array, 'AND pt.proposalid = ? )'); + array_push($args, $person_id); + array_push($args, $value); + }; + + array_push($where_arr, '('.implode(' OR ', $where_propid_personid_array).')'); + + + } else { + $where_propid_array = array(); + + foreach ($propid_array as $value) { + array_push($where_propid_array, 'pt.proposalid = ?'); + array_push($args, $value); + }; + + array_push($where_arr, '('.implode(' OR ', $where_propid_array).')'); + + } + + + // [VALUE, ORDER] + + if ($this->has_arg('sample')) { + $sample_args = explode(',', urldecode($this->arg('sample'))); + + array_push($args, $sample_args[0]); + array_push($where_arr, "lower(sf.name) LIKE lower(CONCAT(CONCAT('%',?),'%')) ESCAPE '$' "); + + if (isset($sample_args[1]) == 'desc' || isset($sample_args[1]) == 'asc') { + array_push($order_arr, 'sf.name '.$sample_args[2]); + } + + } + + + if ($this->has_arg('filetemp')) { + $filetemp_args = explode(',', urldecode($this->arg('filetemp'))); + + array_push($args, $filetemp_args[0]); + array_push($where_arr, "lower(sf.fileTemplate) LIKE lower(CONCAT(CONCAT('%',?),'%')) ESCAPE '$' "); + + if (isset($filetemp_args[1]) == 'desc' || isset($filetemp_args[1]) == 'asc') { + array_push($order_arr, 'sf.fileTemplate '.$filetemp_args[1]); + } + + } + + + foreach ($this->string_arg_types as $key => $value) { + + if ($this->has_arg($key)) { + + $temp_array = array(); + + $temp_args = preg_split('/[,]+(?![^\[]*\])/', urldecode($this->arg($key))); + + $temp_values = explode(',', str_replace(array('[',']'),'', $temp_args[0])); + + foreach ($temp_values as $temp_value) { + array_push($temp_array, $value['where']); + array_push($args, $temp_value); + } + + array_push($where_arr, '('.implode(" OR ", $temp_array).')'); + + if (isset($temp_args[1]) == 'desc' || isset($temp_args[1]) == 'asc') { + array_push($order_arr, $value['order'].$temp_args[1]); + } + } + } + + + // [VALUE, OPERAND, ORDER] + + foreach ($this->val_arg_types as $key => $value) { + + if ($this->has_arg($key)) { + $temp_args = explode(',', $this->arg($key)); + + array_push($args, $temp_args[0]); + + if (in_array($temp_args[1], $op_array) ) { + array_push($where_arr, $value['arg'].$temp_args[1].' ?'); + } + + + if (isset($temp_args[2]) == 'desc' || isset($temp_args[2]) == 'asc') { + array_push($order_arr, $value['arg'].$temp_args[2]); + } + + } + + } + + + // AND is the delimieter between seperate queries, converted to string + $where = implode(" AND ", $where_arr); + + if (count($order_arr) > 0) { + $order = implode(", ", $order_arr); + } + + + // add multiselect params to end of where clause. + // $where = $where.$pp_where.$sg_where.$BEAMLINENAME_where; + + // get tot query + $tot_args = $args; + + $tot = $this->summarydb->pq( + "SELECT COUNT(sf.autoProcIntegrationId) as TOT + FROM SummaryFact sf + JOIN ProposalDimension pt on pt.proposalDimId = sf.proposalDimId + JOIN VisitDimension vt on vt.sessionDimId = sf.sessionDimId + JOIN ProcessingProgramDimension ppt on ppt.processingProgramsDimId = sf.processingProgramsDimId + JOIN SpaceGroupDimension sgt on sgt.spaceGroupDimId = sgt.spaceGroupDimId + WHERE $where + GROUP BY sf.datacollectionId" + , $tot_args); + + $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; + + // paginate + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; + $start = $pg*$pp; + $end = $pg*$pp+$pp; + + $st = sizeof($args)+1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $pgs = intval($tot/$pp); + if ($tot % $pp != 0) $pgs++; + + + $rows = $this->summarydb->paginate( + "SELECT + pt.prop, + sf.autoProcIntegrationId, + sf.dataCollectionId, + sf.personId, + sf.visit_number, + sf.startTime, + vt.beamLineName, + sf.comments, + GROUP_CONCAT(COALESCE(sf.fileTemplate, 'NULL')) as FILETEMPLATE, + GROUP_CONCAT(COALESCE(sf.name, 'NULL')) as SAMPLENAME, + GROUP_CONCAT(COALESCE(sgt.spaceGroup, 'NULL')) as SPACEGROUP, + GROUP_CONCAT(COALESCE(ppt.processingPrograms, 'NULL')) as PROCESSINGPROGRAMS, + GROUP_CONCAT(COALESCE(sf.refinedCell_a, 'NULL')) as REFINEDCELL_A, + GROUP_CONCAT(COALESCE(sf.refinedCell_b, 'NULL')) as REFINEDCELL_B, + GROUP_CONCAT(COALESCE(sf.refinedCell_c, 'NULL')) as REFINEDCELL_C, + GROUP_CONCAT(COALESCE(sf.refinedCell_alpha, 'NULL')) as REFINEDCELL_ALPHA, + GROUP_CONCAT(COALESCE(sf.refinedCell_beta, 'NULL')) as REFINEDCELL_BETA, + GROUP_CONCAT(COALESCE(sf.refinedCell_gamma, 'NULL')) as REFINEDCELL_GAMMA, + GROUP_CONCAT(COALESCE(sf.resolutionLimitHighOuter, 'NULL')) as RESOLUTIONLIMITHIGHOUTER, + GROUP_CONCAT(COALESCE(sf.rMeasWithinIPlusIMinusInner, 'NULL')) as RMEASWITHINIPLUSIMINUSINNER, + GROUP_CONCAT(COALESCE(sf.resIOverSigI2Overall, 'NULL')) as RESIOVERSIGI2OVERALL, + GROUP_CONCAT(COALESCE(sf.ccAnomalousInner, 'NULL')) as CCANOMALOUSINNER, + GROUP_CONCAT(COALESCE(sf.ccAnomalousOverall, 'NULL')) as CCANOMALOUSOVERALL, + GROUP_CONCAT(COALESCE(sf.rFreeValueStartInner, 'NULL')) as RFREEVALUESTARTINNER, + GROUP_CONCAT(COALESCE(sf.rFreeValueEndInner, 'NULL')) as RFREEVALUEENDINNER, + GROUP_CONCAT(COALESCE(sf.noofblobs, 'NULL')) as NOOFBLOBS + FROM SummaryFact sf + JOIN ProposalDimension pt on pt.proposalDimId = sf.proposalDimId + JOIN VisitDimension vt on vt.sessionDimId = sf.sessionDimId + JOIN ProcessingProgramDimension ppt on ppt.processingProgramsDimId = sf.processingProgramsDimId + JOIN SpaceGroupDimension sgt on sgt.spaceGroupDimId = sf.spaceGroupDimId + WHERE $where + GROUP BY sf.dataCollectionId + ORDER BY $order " + , $args); + + + if (!$rows) { + $this->_error($this->arg('TITLE') . ' could not be found anywhere!', 404); + } + + // sql query output + + // $this->_output(array('data' => $where, 'args' => $args)); + $this->_output(array('data' => $rows, 'total' => $tot )); + // $this->_output(array('data' => $rows, 'where' => $where, 'order' => $order, 'args' => $args)); + + + + } + + function _get_proposal() { + $this->error_on_no_summary_db(); + $args = array(); + $where = "WHERE 1=1"; + + if (!$this->staff) { + $person_id = $this->user->personId; + + // $person_id = 16565; + + $where_person = "sf.personId = ".$person_id; + + $rows = $this->summarydb->pq( + "SELECT pt.prop, pt.proposalid + FROM ProposalDimension pt + JOIN SummaryFact sf on pt.proposalDimId = sf.proposalDimId + WHERE $where_person + GROUP BY pt.proposalid"); + + $this->_output($rows); + + } else { + + $tot = $this->summarydb->pq( + "SELECT COUNT(proposalid) as TOT + FROM ProposalDimension pt + $where", $args); + + + $tot = sizeof($tot) ? intval($tot[0]['TOT']) : 0; + + // paginate + $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; + $pg = $this->has_arg('page') ? $this->arg('page')-1 : 0; + $start = $pg*$pp; + $end = $pg*$pp+$pp; + + $st = sizeof($args)+1; + $en = $st + 1; + array_push($args, $start); + array_push($args, $end); + + $pgs = intval($tot/$pp); + if ($tot % $pp != 0) $pgs++; + + $order = 'p.proposalid DESC'; + + $rows = $this->summarydb->paginate( + "SELECT prop, proposalid + FROM ProposalDimension pt + $where", $args); + + $this->_output($rows); + + } + + + + } + + function _get_spacegroup() { + $this->error_on_no_summary_db(); + $rows = $this->summarydb->pq( + "SELECT spaceGroup + FROM SpaceGroupDimension sgt"); + + $this->_output($rows); + + } + + function _get_processingprogram() { + $this->error_on_no_summary_db(); + $rows = $this->summarydb->pq( + "SELECT processingPrograms + FROM ProcessingProgramDimension ppt"); + + $this->_output($rows); + + } + + function _get_beamline() { + $this->error_on_no_summary_db(); + $rows = $this->summarydb->pq( + "SELECT beamLineName + FROM VisitDimension vt"); + + $this->_output($rows); + + } + + + + +} + diff --git a/api/src/Page/Users.php b/api/src/Page/Users.php deleted file mode 100644 index 9948b4625..000000000 --- a/api/src/Page/Users.php +++ /dev/null @@ -1,496 +0,0 @@ - '\d+', - 'pid' => '\d+', - 'pjid' => '\d+', - 'peid' => '\d+', - 'uid' => '\d+', - 'sid' => '\d+', - 'visit' => '\w+\d+-\d+', - 'location' => '(\w|-|\/)+', - 'all' => '\d', - 'login' => '\d', - - 'NAME' => '\w+', - - 'TYPE' => '\w+', - 'DESCRIPTION' => '(\w|\s)+', - - - 'PERSONID' => '\d+', - 'FAMILYNAME' => '([\w-])+', - 'GIVENNAME' => '([\w-])+', - 'PHONENUMBER' => '.*', - 'EMAILADDRESS' => '.*', - 'LABNAME' => '([\w\s-])+', - 'ADDRESS' => '([\w\s-\n])+', - 'COUNTRY' => '([\w\s-])+', - 'CITY' => '([\w\s-])+', - 'POSTCODE' => '([\w\s-])+', - 'LOGIN' => '\w+', - 'PASSWORD' => '.*', - ); - - - public static $dispatch = array(array('(/:PERSONID)', 'get', '_get_users'), - array('/:PERSONID', 'patch', '_update_user'), - array('/', 'post', '_add_user'), - - array('/current', 'get', '_get_current_user'), - - array('/login', 'get', '_login'), - array('/log(/)', 'post', '_log_action'), - array('/time', 'get', '_get_time'), - - array('/groups(/:gid)', 'get', '_get_groups'), - array('/groups', 'post', '_add_group'), - array('/groups/:gid', 'put', '_update_group'), - - array('/groups/:gid/permission/:pid', 'post', '_add_group_permission'), - array('/groups/:gid/permission/:pid', 'delete', '_remove_group_permission'), - - array('/groups/:gid/users/:peid', 'post', '_add_group_user'), - array('/groups/:gid/users/:peid', 'delete', '_remove_group_user'), - - array('/permissions(/:pid)(/group/:gid)', 'get', '_get_permissions'), - array('/permissions', 'post', '_add_permission'), - array('/permissions/:pid', 'put', '_update_permission'), - ); - - - # ------------------------------------------------------------------------ - # Helpers for backbone application - function _get_current_user() { - $this->_output(array('personid' => $this->user->personid, 'user' => $this->user->login, 'givenname' => $this->user->givenname, - 'permissions' => $this->user->perms, - 'is_staff' => $this->staff, - 'visits' => $this->visits, - 'ty' => $this->ty)); - } - - function _login() { - } - - function _log_action() { - if (!$this->has_arg('location')) $this->_error('No location specified'); - $this->log_action(1, $this->arg('location')); - print $this->arg('location'); - } - - - # ------------------------------------------------------------------------ - # Get current time - function _get_time() { - $d = new \DateTime("now"); - $this->_output(array('TIME' => $d->format('D M d Y H:i:s (\G\M\TO)'))); - } - - - - function _get_groups() { - $this->user->can('manage_groups'); - - $where = ''; - $args = array(); - - if ($this->has_arg('gid')) { - $where = 'WHERE g.usergroupid=:1'; - array_push($args, $this->arg('gid')); - } - - $groups = $this->db->pq("SELECT g.usergroupid, g.name, count(uhp.personid) as users - FROM usergroup g - LEFT OUTER JOIN usergroup_has_person uhp ON uhp.usergroupid = g.usergroupid - $where - GROUP BY g.usergroupid, g.name - ORDER BY g.name", $args); - if ($this->has_arg('gid')) { - if (sizeof($groups)) $this->_output($groups[0]); - else $this->_error('No such group'); - } else $this->_output($groups); - } - - - function _add_group() { - $this->user->can('manage_groups'); - - if (!$this->has_arg('NAME')) $this->_error('No group name'); - - $this->db->pq('INSERT INTO usergroup (usergroupid,name) VALUES (s_usergroup.nextval,:1) RETURNING usergroupid INTO :id',array($this->arg('NAME'))); - $this->_output(array('USERGROUPID' => $this->db->id())); - } - - - function _update_group() { - $this->user->can('manage_groups'); - $group = $this->db->pq("SELECT usergroupid FROM usergroup WHERE usergroupid = :1", array($this->arg('gid'))); - - if (!sizeof($group)) $this->_error('No such group'); - $this->db->pq('UPDATE usergroup SET name=:1 WHERE usergroupid=:2', array($this->arg('NAME'), $this->arg('gid'))); - $this->_output(array('NAME' => $this->arg('NAME'))); - } - - - - - function _add_group_permission() { - $this->user->can('manage_groups'); - - $group = $this->db->pq("SELECT usergroupid FROM usergroup WHERE usergroupid=:1", array($this->arg('gid'))); - if (!sizeof($group)) $this->_error('No such group'); - $perm = $this->db->pq("SELECT permissionid FROM permission WHERE permissionid=:1", array($this->arg('pid'))); - if (!sizeof($perm)) $this->_error('No such permission'); - - $chk = $this->db->pq("SELECT usergroupid FROM usergroup_has_permission WHERE usergroupid=:1 and permissionid=:2", array($this->arg('gid'), $this->arg('pid'))); - if (sizeof($chk)) $this->_error('That group already has the supplied permission'); - - $this->db->pq("INSERT INTO usergroup_has_permission (usergroupid, permissionid) VALUES (:1,:2)", array($this->arg('gid'), $this->arg('pid'))); - $this->_output(array('USERGROUPID' => $this->arg('gid'), 'PERMISSIONID' => $this->arg('pid'))); - } - - - function _remove_group_permission() { - $this->user->can('manage_groups'); - - $chk = $this->db->pq("SELECT usergroupid FROM usergroup_has_permission WHERE usergroupid=:1 and permissionid=:2", array($this->arg('gid'), $this->arg('pid'))); - if (!sizeof($chk)) $this->_error('That group does not have the supplied permission'); - - $this->db->pq("DELETE FROM usergroup_has_permission WHERE usergroupid=:1 and permissionid=:2", array($this->arg('gid'), $this->arg('pid'))); - $this->_output(1); - } - - - - function _get_users() { - // $this->user->can('manage_groups'); - - $args = array(); - $where = 'p.login IS NOT NULL'; - $join = ''; - $extc = ''; - $group = 'GROUP BY p.personid'; - - if ($this->has_arg('all')) { - $where = '1=1'; - } - - if ($this->has_arg('pid')) { - $where .= ' AND (prhp.proposalid=:'.(sizeof($args)+1).' OR lc.proposalid=:'.(sizeof($args)+2).' OR p.personid=:'.(sizeof($args)+3).')'; - array_push($args, $this->proposalid); - array_push($args, $this->proposalid); - array_push($args, $this->user->personid); - } - - if ($this->has_arg('PERSONID')) { - $where = '1=1'; - - $where .= ' AND p.personid=:'.(sizeof($args)+1); - array_push($args, $this->arg('PERSONID')); - - if (!$this->user->has('manage_users')) { - $where .= ' AND (prhp.proposalid=:'.(sizeof($args)+1).' OR lc.proposalid=:'.(sizeof($args)+2).' OR p.personid=:'.(sizeof($args)+3).')'; - array_push($args, $this->proposalid); - array_push($args, $this->proposalid); - array_push($args, $this->user->personid); - } - } - - if (!$this->staff && !$this->has_arg('visit') && !$this->has_arg('pid')) { - $where .= ' AND (prhp.proposalid=:'.(sizeof($args)+1).' OR lc.proposalid=:'.(sizeof($args)+2).' OR p.personid=:'.(sizeof($args)+3).')'; - array_push($args, $this->proposalid); - array_push($args, $this->proposalid); - array_push($args, $this->user->personid); - } - - if ($this->has_arg('gid')) { - $join = 'INNER JOIN usergroup_has_person uhp ON uhp.personid = p.personid'; - $where .= ' AND uhp.usergroupid=:'.(sizeof($args)+1); - array_push($args, $this->arg('gid')); - } - - if ($this->has_arg('login')) { - $where .= ' AND p.login IS NOT NULL'; - } - - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%',:".$st."),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%',:".($st+1)."),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%',:".($st+2)."),'%')))"; - for ($i = 0; $i < 3; $i++) array_push($args, $this->arg('s')); - } - - if ($this->has_arg('sid')) { - $join = 'INNER JOIN blsession_has_person shp ON shp.personid = p.personid'; - $where .= ' AND shp.sessionid=:'+(sizeof($args)+1); - array_push($args, $this->arg('sid')); - } - - if ($this->has_arg('visit')) { - $extc = "count(ses.sessionid) as visits, TO_CHAR(max(ses.startdate), 'DD-MM-YYYY') as last, shp.remote, shp.role,"; - $join = 'INNER JOIN session_has_person shp ON shp.personid = p.personid - INNER JOIN blsession s ON shp.sessionid = s.sessionid - INNER JOIN proposal pr ON pr.proposalid = s.proposalid - - LEFT OUTER JOIN session_has_person shp2 ON p.personid = shp2.personid - LEFT OUTER JOIN blsession ses ON ses.sessionid = shp2.sessionid AND ses.startdate < s.startdate'; - $where .= " AND shp.remote IS NOT NULL AND CONCAT(CONCAT(CONCAT(pr.proposalcode,pr.proposalnumber), '-'), s.visit_number) LIKE :".(sizeof($args)+1); - $group = 'GROUP BY p.personid, p.givenname, p.familyname, p.login'; - array_push($args, $this->arg('visit')); - } - - if ($this->has_arg('pjid')) { - $join = ' INNER JOIN project_has_person php ON p.personid = php.personid'; - $where .= ' AND php.projectid=:'.(sizeof($args)+1); - $extc = "CONCAT(CONCAT(p.personid, '-'), php.projectid) as ppid,"; - array_push($args, $this->arg('pjid')); - } - - - $tot = $this->db->pq("SELECT count(distinct p.personid) as tot - FROM person p - LEFT OUTER JOIN proposalhasperson prhp ON prhp.personid = p.personid - LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid - $join - WHERE $where", $args); - $tot = intval($tot[0]['TOT']); - - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - array_push($args, $start); - array_push($args, $end); - - - $order = 'p.familyname,p.givenname'; - if ($this->has_arg('sort_by')) { - $cols = array('LOGIN' => 'p.login', 'GIVENNAME' => 'p.givenname', 'FAMILYNAME' => 'p.familyname'); - $dir = $this->has_arg('order') ? ($this->arg('order') == 'asc' ? 'ASC' : 'DESC') : 'ASC'; - if (array_key_exists($this->arg('sort_by'), $cols)) $order = $cols[$this->arg('sort_by')].' '.$dir; - } - - $rows = $this->db->paginate("SELECT $extc p.personid, p.givenname, p.familyname, CONCAT(CONCAT(p.givenname, ' '), p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, '' as postcode, l.country - FROM person p - LEFT OUTER JOIN proposalhasperson prhp ON prhp.personid = p.personid - LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid - LEFT OUTER JOIN laboratory l ON l.laboratoryid = p.laboratoryid - $join - WHERE $where - $group - ORDER BY $order", $args); - - foreach ($rows as &$r) { - if ($r['PERSONID'] == $this->user->personid) $r['FULLNAME'] .= ' [You]'; - } - - if ($this->has_arg('PERSONID')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such user'); - - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); - - } - - - function _check_login() { - if (!$this->user->can('manage_users')) $this->_error('No access'); - if (!$this->has_arg('LOGIN')) $this->_error('No login specified'); - - $person = $this->db->pq("SELECT login FROM person WHERE login=:1", array($this->arg('LOGIN'))); - - if (!sizeof($person)) $this->_error('Login not used'); - else $this->_output(new \stdClass); - } - - - function _add_user() { - if (!$this->user->can('manage_users')) $this->_error('No access'); - - if (!$this->has_arg('LOGIN')) $this->_error('No login specified'); - if (!$this->has_arg('GIVENNAME')) $this->_error('No given name specified'); - if (!$this->has_arg('FAMILYNAME')) $this->_error('No family name specified'); - - $email = $this->has_arg('EMAILADDRESS') ? $this->arg('EMAILADDRESS') : null; - $this->db->pq("INSERT INTO person (login, givenname, familyname, emailaddress) VALUES (:1, :2, :3, :4)", - array($this->arg('LOGIN'), $this->arg('GIVENNAME'), $this->arg('FAMILYNAME'), $email)); - - $this->_output(array('PERSONID' => $this->db->id())); - } - - - - function _update_user() { - if (!$this->has_arg('PERSONID')) $this->_error('No person specified'); - - // $this->db->set_debug(True); - $person = $this->db->pq("SELECT p.personid, p.laboratoryid - FROM person p - LEFT OUTER JOIN proposalhasperson php ON php.personid = p.personid - LEFT OUTER JOIN labcontact lc ON lc.personid = p.personid - WHERE (p.personid=:1 OR php.proposalid=:2 OR lc.proposalid=:3) AND p.personid=:4", array($this->user->personid, $this->proposalid, $this->proposalid, $this->arg('PERSONID'))); - - if (!sizeof($person)) $this->_error('No such person'); - $person = $person[0]; - - # Update person - $pfields = array('FAMILYNAME', 'GIVENNAME', 'PHONENUMBER', 'EMAILADDRESS'); - foreach ($pfields as $i => $f) { - if ($this->has_arg($f)) { - $v = $this->arg($f); - - $this->db->pq('UPDATE person SET '.$f.'=:1 WHERE personid=:2', array($v, $person['PERSONID'])); - $this->_output(array($f => $this->arg($f))); - } - } - - # Update laboratory - $lfields = array('LABNAME', 'ADDRESS', 'CITY', 'POSTCODE', 'COUNTRY'); - foreach ($lfields as $i => $f) { - if ($this->has_arg($f)) { - $c = $f == 'LABNAME' ? 'NAME' : $f; - - if (!$person['LABORATORYID']) { - $this->db->pq("INSERT INTO laboratory ($c) VALUES (:1)", array($this->arg($f))); - $person['LABORATORYID'] = $this->db->id(); - $this->db->pq("UPDATE person SET laboratoryid=:1 WHERE personid=:2", array($person['LABORATORYID'], $this->arg('PERSONID'))); - - } else $this->db->pq('UPDATE laboratory SET '.$c.'=:1 WHERE laboratoryid=:2', array($this->arg($f), $person['LABORATORYID'])); - $this->_output(array($f => $this->arg($f))); - } - } - } - - - - function _add_group_user() { - $this->user->can('manage_groups'); - - $user = $this->db->pq("SELECT personid FROM person WHERE personid=:1", array($this->arg('peid'))); - if (!sizeof($user)) $this->_error('No such user'); - $group = $this->db->pq("SELECT usergroupid FROM usergroup WHERE usergroupid=:1", array($this->arg('gid'))); - if (!sizeof($group)) $this->_error('No such group'); - - $chk = $this->db->pq("SELECT personid FROM usergroup_has_person WHERE usergroupid=:1 and personid=:2", array($this->arg('gid'), $this->arg('peid'))); - if (sizeof($chk)) $this->_error('That group already contains the supplied person'); - - $this->db->pq("INSERT INTO usergroup_has_person (usergroupid, personid) VALUES (:1,:2)", array($this->arg('gid'), $this->arg('peid'))); - $this->_output(array('USERGROUPID' => $this->arg('gid'), 'PERSONID' => $this->arg('peid'))); - } - - - function _remove_group_user() { - $this->user->can('manage_groups'); - - $chk = $this->db->pq("SELECT usergroupid FROM usergroup_has_person WHERE usergroupid=:1 and personid=:2", array($this->arg('gid'), $this->arg('peid'))); - if (!sizeof($chk)) $this->_error('That group does not have the supplied permission'); - - $this->db->pq("DELETE FROM usergroup_has_person WHERE usergroupid=:1 and personid=:2", array($this->arg('gid'), $this->arg('peid'))); - $this->_output(1); - } - - - - - - function _get_permissions() { - $this->user->can('manage_perms'); - - $args = array(); - $where = ''; - $join = ''; - - if ($this->has_arg('gid')) { - $join = 'INNER JOIN usergroup_has_permission uhp ON uhp.permissionid = p.permissionid'; - $where = 'AND uhp.usergroupid=:'.(sizeof($args)+1);; - array_push($args, $this->arg('gid')); - } - - if ($this->has_arg('pid')) { - $where = 'AND p.permissionid=:'.(sizeof($args)+1); - array_push($args, $this->arg('pid')); - } - - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND (lower(p.type) LIKE lower(CONCAT(CONCAT('%',:".$st."),'%')))"; - array_push($args, $this->arg('s')); - } - - $tot = $this->db->pq("SELECT count(p.permissionid) as tot - FROM permission p - $join - WHERE 1=1 $where", $args); - $tot = intval($tot[0]['TOT']); - - - $start = 0; - $pp = $this->has_arg('per_page') ? $this->arg('per_page') : 15; - $end = $pp; - - if ($this->has_arg('page')) { - $pg = $this->arg('page') - 1; - $start = $pg*$pp; - $end = $pg*$pp+$pp; - } - - $st = sizeof($args)+1; - array_push($args, $start); - array_push($args, $end); - - - $rows = $this->db->paginate("SELECT p.permissionid, p.type, p.description - FROM permission p - $join - WHERE 1=1 $where - ORDER BY p.type", $args); - - - if ($this->has_arg('pid')) { - if (sizeof($rows)) $this->_output($rows[0]); - else $this->_error('No such permission'); - } else $this->_output(array('total' => $tot, - 'data' => $rows, - )); - } - - - function _add_permission() { - $this->user->can('manage_perms'); - - if (!$this->has_arg('TYPE')) $this->_error('No permission type'); - $desc = $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : ''; - - $this->db->pq('INSERT INTO permission (permissionid,type,description) VALUES (s_permission.nextval,:1,:2) RETURNING permissionid INTO :id',array($this->arg('TYPE'), $desc)); - $this->_output(array('PERMISSIONID' => $this->db->id())); - } - - - function _update_permission() { - $this->user->can('manage_perms'); - - $perm = $this->db->pq("SELECT permissionid FROM permission WHERE permissionid = :1", array($this->arg('pid'))); - if (!sizeof($perm)) $this->_error('No such permission'); - - $desc = $this->has_arg('DESCRIPTION') ? $this->arg('DESCRIPTION') : ''; - $this->db->pq('UPDATE permission SET type=:1, description=:2 WHERE permissionid=:3', array($this->arg('TYPE'), $desc, $this->arg('pid'))); - - $this->_output(array('TYPE' => $this->arg('TYPE'), $desc)); - } -} diff --git a/api/src/Page/Vstat.php b/api/src/Page/Vstat.php index 06f75b433..64a476873 100644 --- a/api/src/Page/Vstat.php +++ b/api/src/Page/Vstat.php @@ -7,72 +7,79 @@ class Vstat extends Page { - - - public static $arg_list = array( - 'visit' => '\w+\d+-\d+', - 'ty' => '\w+', - 'runid' => '\d+', - 'group_by' => '\w+', - 'proposalcode' => '\w+', - 'proposal' => '\w+', - 'scheduled' => '\d', - 'bl' => '[\w-]+', - 'download' => '\d', - 'data' => '\d', - 'history' => '\d', - ); - public static $dispatch = array(array('/breakdown(/:visit)', 'get', '_visit_breakdown'), - array('/hrs(/:visit)', 'get', '_hourlies'), - array('/pies(/:visit)', 'get', '_pies'), - array('/ehc/:visit', 'get', '_ehc_log'), - array('/call/:visit', 'get', '_callouts'), - array('/errors(/:visit)', 'get', '_error_log'), - array('/overview', 'get', '_overview'), - array('/runs', 'get', '_runs'), - array('/histogram', 'get', '_parameter_histogram'), + public static $arg_list = array( + 'visit' => '\w+\d+-\d+', + 'ty' => '\w+', + 'runid' => '\d+', + 'group_by' => '\w+', + 'proposalcode' => '\w+', + 'proposal' => '\w+', + 'scheduled' => '\d', + 'bl' => '[\w\-]+', + 'download' => '\d', + 'data' => '\d', + 'history' => '\d', + ); - array('/dewars', 'get', '_dewars_breakdown'), - ); + public static $dispatch = array( + array('/breakdown(/:visit)', 'get', '_visit_breakdown'), + array('/hrs(/:visit)', 'get', '_hourlies'), + array('/pies(/:visit)', 'get', '_pies'), + array('/ehc/:visit', 'get', '_ehc_log'), + array('/call/:visit', 'get', '_callouts'), + array('/errors(/:visit)', 'get', '_error_log'), + array('/overview', 'get', '_overview'), + array('/runs', 'get', '_runs'), + array('/histogram', 'get', '_parameter_histogram'), + array('/dewars', 'get', '_dewars_breakdown'), - function __construct() { - call_user_func_array(array('parent', '__construct'), func_get_args()); + ); - global $dhl_user, $dhl_pass, $dhl_env; - $this->dhl = new DHL($dhl_user, $dhl_pass, $dhl_env); - } + var $dhl; + function __construct() + { + call_user_func_array(array('parent', '__construct'), func_get_args()); - - function _visit_breakdown() { - ini_set('memory_limit', '512M'); + global $dhl_user, $dhl_pass, $dhl_env; + $this->dhl = new DHL($dhl_user, $dhl_pass, $dhl_env); + } - if ($this->has_arg('visit')) { - $info = $this->_check_visit(); - $where = 'AND s.sessionid=:1'; - $args = array($info['SID']); - } else if ($this->user->has('all_breakdown')) { - if (!$this->has_arg('runid')) $this->_error('No run specified'); - if (!$this->has_arg('bl')) $this->_error('No beamline specified'); - $info = $this->db->pq("SELECT TO_CHAR(vr.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(vr.enddate, 'DD-MM-YYYY HH24:MI') as en + function _visit_breakdown() + { + ini_set('memory_limit', '512M'); + + if ($this->has_arg('visit')) { + $info = $this->_check_visit(); + $where = 'AND s.sessionid=:1'; + $args = array($info['SID']); + } else if ($this->user->hasPermission('all_breakdown')) { + if (!$this->has_arg('runid')) + $this->_error('No run specified'); + if (!$this->has_arg('bl')) + $this->_error('No beamline specified'); + + $info = $this->db->pq("SELECT TO_CHAR(vr.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(vr.enddate, 'DD-MM-YYYY HH24:MI') as en FROM v_run vr WHERE runid=:1", array($this->arg('runid'))); - if (!sizeof($info)) $this->_error('No such run'); - $info = $info[0]; + if (!sizeof($info)) + $this->_error('No such run'); + $info = $info[0]; - $where = "AND vr.runid=:1 AND s.beamlinename=:2 AND p.proposalcode <> 'cm'"; - $args = array($this->arg('runid'), $this->arg('bl')); - - } else $this->_error('No visit specified'); + $where = "AND vr.runid=:1 AND s.beamlinename=:2 AND p.proposalcode <> 'cm'"; + $args = array($this->arg('runid'), $this->arg('bl')); + } else { + $this->_error('No visit specified'); + } - $dc = $this->db->pq("SELECT IF(dc.chistart IS NULL, 0, dc.chistart) as chistart, dc.kappastart, dc.phistart, dc.wavelength, dc.beamsizeatsamplex, dc.beamsizeatsampley, dc.datacollectionid as id, TO_CHAR(dc.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(dc.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, dc.runstatus, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, pr.proteinid, pr.acronym as protein, smp.name as sample + $dc = $this->db->pq("SELECT IF(dc.chistart IS NULL, 0, dc.chistart) as chistart, dc.kappastart, dc.phistart, dc.wavelength, dc.beamsizeatsamplex, dc.beamsizeatsampley, dc.datacollectionid as id, TO_CHAR(dc.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(dc.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, dc.runstatus, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, pr.proteinid, pr.acronym as protein, smp.name as sample FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid @@ -82,23 +89,35 @@ function _visit_breakdown() { LEFT OUTER JOIN crystal c ON c.crystalid = smp.crystalid LEFT OUTER JOIN protein pr ON pr.proteinid = c.proteinid WHERE 1=1 $where ORDER BY dc.starttime", $args); - - $dcf = $this->db->pq("SELECT COUNT(dc.datacollectionid) as count + + $dcf = $this->db->pq("SELECT COUNT(dc.datacollectionid) as count, + COUNT(distinct dc.blsampleid) as samplecount FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE dc.overlap = 0 AND dc.axisrange > 0 $where", $args); - $dcs = $this->db->pq("SELECT COUNT(dc.datacollectionid) as count + + $grid = $this->db->pq("SELECT COUNT(dc.datacollectionid) as count, + COUNT(distinct dc.blsampleid) as samplecount + FROM datacollection dc + INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid + INNER JOIN blsession s ON s.sessionid = dcg.sessionid + INNER JOIN proposal p ON p.proposalid = s.proposalid + INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate + WHERE dc.overlap = 0 AND dc.axisrange = 0 $where", $args); + + $dcs = $this->db->pq("SELECT COUNT(dc.datacollectionid) as count, + COUNT(distinct dc.blsampleid) as samplecount FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE dc.overlap != 0 $where", $args); - - $robot = $this->db->pq("SELECT r.status, r.actiontype, TO_CHAR(r.starttimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(r.endtimestamp, 'DD-MM-YYYY HH24:MI:SS') as en, pr.acronym as protein, smp.name as sample + + $robot = $this->db->pq("SELECT r.status, r.actiontype, TO_CHAR(r.starttimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(r.endtimestamp, 'DD-MM-YYYY HH24:MI:SS') as en, pr.acronym as protein, smp.name as sample FROM robotaction r INNER JOIN blsession s ON s.sessionid = r.blsessionid INNER JOIN proposal p ON p.proposalid = s.proposalid @@ -108,7 +127,7 @@ function _visit_breakdown() { LEFT OUTER JOIN protein pr ON pr.proteinid = c.proteinid WHERE r.actiontype='LOAD' $where ORDER BY r.endtimestamp DESC", $args); - $edge = $this->db->pq("SELECT e.energyscanid as id, TO_CHAR(e.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(e.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, CONCAT(p.proposalnumber, p.proposalcode) as prop, pr.acronym as protein, smp.name as sample + $edge = $this->db->pq("SELECT e.energyscanid as id, TO_CHAR(e.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(e.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, CONCAT(p.proposalnumber, p.proposalcode) as prop, pr.acronym as protein, smp.name as sample FROM energyscan e INNER JOIN blsession s ON s.sessionid = e.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid @@ -118,7 +137,7 @@ function _visit_breakdown() { LEFT OUTER JOIN protein pr ON pr.proteinid = c.proteinid WHERE 1=1 $where ORDER BY e.endtime DESC", $args); - $fl = $this->db->pq("SELECT f.xfefluorescencespectrumid as id, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, CONCAT(p.proposalnumber, p.proposalcode) as prop, pr.acronym as protein, smp.name as sample + $fl = $this->db->pq("SELECT f.xfefluorescencespectrumid as id, TO_CHAR(f.starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(f.endtime, 'DD-MM-YYYY HH24:MI:SS') as en, CONCAT(p.proposalnumber, p.proposalcode) as prop, pr.acronym as protein, smp.name as sample FROM xfefluorescencespectrum f INNER JOIN blsession s ON s.sessionid = f.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid @@ -127,9 +146,9 @@ function _visit_breakdown() { LEFT OUTER JOIN crystal c ON c.crystalid = smp.crystalid LEFT OUTER JOIN protein pr ON pr.proteinid = c.proteinid WHERE 1=1 $where ORDER BY f.endtime DESC", $args); - - if ($this->has_arg('visit')) { - $ai = $this->db->pq("SELECT dc.datacollectionid as id, TO_CHAR(dc.endtime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(max(sc.bltimestamp), 'DD-MM-YYYY HH24:MI:SS') as en + + if ($this->has_arg('visit')) { + $ai = $this->db->pq("SELECT dc.datacollectionid as id, TO_CHAR(dc.endtime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(max(sc.bltimestamp), 'DD-MM-YYYY HH24:MI:SS') as en FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid @@ -138,8 +157,8 @@ function _visit_breakdown() { INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE 1=1 $where GROUP BY dc.datacollectionid, dc.endtime ORDER BY dc.endtime DESC", $args); - - $cent = $this->db->pq("SELECT * FROM (SELECT TO_CHAR(r.endtimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(min(dc.starttime), 'DD-MM-YYYY HH24:MI:SS') as en, TIMESTAMPDIFF('SECOND', CAST(r.endtimestamp as DATE), min(dc.starttime)) as dctime + + $cent = $this->db->pq("SELECT * FROM (SELECT TO_CHAR(r.endtimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(min(dc.starttime), 'DD-MM-YYYY HH24:MI:SS') as en, TIMESTAMPDIFF('SECOND', CAST(r.endtimestamp as DATE), min(dc.starttime)) as dctime FROM robotaction r INNER JOIN datacollection dc ON r.blsampleid = dc.blsampleid AND r.endtimestamp < dc.starttime INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid @@ -149,9 +168,9 @@ function _visit_breakdown() { WHERE 1=1 $where GROUP BY r.endtimestamp ORDER BY r.endtimestamp) inq WHERE dctime < 1000", $args); - $sched = array(); + $sched = array(); - $ctf = $this->db->pq("SELECT TO_CHAR(m.createdtimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, c.astigmatism, c.estimatedresolution, c.estimateddefocus + $ctf = $this->db->pq("SELECT TO_CHAR(m.createdtimestamp, 'DD-MM-YYYY HH24:MI:SS') as st, c.astigmatism, c.estimatedresolution, c.estimateddefocus FROM ctf c INNER JOIN autoprocprogram app ON app.autoprocprogramid = c.autoprocprogramid INNER JOIN motioncorrection mc ON mc.motioncorrectionid = c.motioncorrectionid @@ -163,233 +182,281 @@ function _visit_breakdown() { INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE 1=1 $where ORDER BY m.createdtimestamp", $args); - } else { - $ai = array(); - $cent = array(); - $ctf = array(); - - $sched = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI:SS') as en, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI:SS') as st, p.title, s.scheduled, p.proposalcode + $newargs = array($info['SID'], $info['SID']); + $missed = $this->db->pq("SELECT COUNT(smp.name) as samplecount, + GROUP_CONCAT(smp.name SEPARATOR ', ') as missed + FROM blsample smp + INNER JOIN robotaction r ON r.blsampleid = smp.blsampleid + INNER JOIN blsession s ON s.sessionid = r.blsessionid + WHERE 1=1 $where + AND smp.blsampleid not in + (SELECT blsmp.blSampleId FROM blsample blsmp + INNER JOIN datacollection dc ON dc.blsampleid = blsmp.blsampleid + WHERE dc.sessionId=:2)", $newargs); + } else { + $ai = array(); + $cent = array(); + $ctf = array(); + $missed = array(); + + $sched = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI:SS') as en, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI:SS') as st, p.title, s.scheduled, p.proposalcode FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE 1=1 $where ORDER BY p.proposalcode, s.startdate", $args); - } + } - # Get Faults - $faultl = $this->db->pq("SELECT f.faultid, f.beamtimelost, f.title, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI:SS') as en + # Get Faults + $faultl = $this->db->pq("SELECT f.faultid, f.beamtimelost, f.title, TO_CHAR(f.beamtimelost_starttime, 'DD-MM-YYYY HH24:MI:SS') as st, TO_CHAR(f.beamtimelost_endtime, 'DD-MM-YYYY HH24:MI:SS') as en FROM bf_fault f INNER JOIN blsession s ON f.sessionid = s.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN v_run vr ON s.startdate BETWEEN vr.startdate AND vr.enddate WHERE f.beamtimelost=1 $where", $args); - - $info['DC_FULL'] = sizeof($dcf) ? $dcf[0]['COUNT'] : 0; - $info['DC_SCREEN'] = sizeof($dcs) ? $dcs[0]['COUNT'] : 0; - $info['DC_TOT'] = sizeof($dc); - $info['DC_GRID'] = $info['DC_TOT'] - $info['DC_SCREEN'] - $info['DC_FULL']; - $info['DC_STOPPED'] = 0; - $info['E_TOT'] = sizeof($edge); - $info['FL_TOT'] = sizeof($fl); - $info['R_TOT'] = sizeof($robot); - $info['F_TOT'] = sizeof($faultl); - - if ($info['DC_TOT'] + $info['E_TOT'] + $info['R_TOT'] == 0) $this->_error('No Data'); - - $data = array(); - $linecols = array('ENERGY', 'BEAMSIZEATSAMPLEX', 'BEAMSIZEATSAMPLEY', 'KAPPASTART', 'PHISTART', 'CHISTART'); - $lines = array(); - foreach ($linecols as $c) array_push($lines, array('data' => array())); + $info['DC_FULL'] = sizeof($dcf) ? $dcf[0]['COUNT'] : 0; + $info['DC_FULL_SAMPLES'] = $dcf[0]['SAMPLECOUNT'] ? $dcf[0]['SAMPLECOUNT'] : 0; + $info['DC_SCREEN'] = sizeof($dcs) ? $dcs[0]['COUNT'] : 0; + $info['DC_SCREEN_SAMPLES'] = $dcs[0]['SAMPLECOUNT'] ? $dcs[0]['SAMPLECOUNT'] : 0; + $info['DC_TOT'] = sizeof($dc); + $info['DC_GRID'] = sizeof($grid) ? $grid[0]['COUNT'] : 0; + $info['DC_GRID_SAMPLES'] = $grid[0]['SAMPLECOUNT'] ? $grid[0]['SAMPLECOUNT'] : 0; + $info['DC_MISSED'] = sizeof($missed) ? $missed[0]['MISSED'] : ''; + $info['DC_MISSED_SAMPLES'] = sizeof($missed) ? $missed[0]['SAMPLECOUNT'] : 0; + $info['DC_STOPPED'] = 0; + $info['E_TOT'] = sizeof($edge); + $info['FL_TOT'] = sizeof($fl); + $info['R_TOT'] = sizeof($robot); + $info['F_TOT'] = sizeof($faultl); + + if ($info['DC_TOT'] + $info['E_TOT'] + $info['R_TOT'] == 0) + $this->_error('No Data'); + + $data = array(); + + if ($info['DC_TOT'] + $info['E_TOT'] + $info['R_TOT'] == 0) + $this->_error('No Data'); + + $data = array(); + + $linecols = array('ENERGY', 'BEAMSIZEATSAMPLEX', 'BEAMSIZEATSAMPLEY', 'KAPPASTART', 'PHISTART', 'CHISTART'); + $lines = array(); + foreach ($linecols as $c) + array_push($lines, array('data' => array())); + + $scattercols = array('ASTIGMATISM', 'ESTIMATEDDEFOCUS', 'ESTIMATEDRESOLUTION'); + $scatters = array(); + foreach ($scattercols as $c) + array_push($scatters, array('data' => array())); + + foreach ($ctf as $c) { + foreach ($scattercols as $i => $f) { + $scatters[$i]['label'] = $f; + if ($i > 0) + $scatters[$i]['yaxis'] = $i + 1; + if (floatval($c[$f]) < 1e38) + array_push($scatters[$i]['data'], array($this->jst($c['ST']), floatval(round($c[$f], 4)))); + } + } + + $queued = 0; + $vis_map = array(); + foreach ($sched as $s) { + if ($s['SCHEDULED'] != '1' || $s['PROPOSALCODE'] == 'lb' || $s['PROPOSALCODE'] == 'nr') { + $vis_map[$s['VISIT']] = $s; + $queued++; + continue; + } + array_push($data, array('data' => array( + array($this->jst($s['ST']), 6, $this->jst($s['ST'])), + array($this->jst($s['EN']), 6, $this->jst($s['ST'])) + ), 'color' => 'purple', 'type' => 'visit', 'status' => $s['VISIT'] . ': ' . $s['TITLE'], 'visit' => $s['VISIT'])); + } - $scattercols = array('ASTIGMATISM', 'ESTIMATEDDEFOCUS', 'ESTIMATEDRESOLUTION'); - $scatters = array(); - foreach ($scattercols as $c) array_push($scatters, array('data' => array())); + $lastend = null; + $lastvisit = null; + $startvisit = null; + foreach ($dc as $d) { - foreach ($ctf as $c) { - foreach ($scattercols as $i => $f) { - $scatters[$i]['label'] = $f; - if ($i > 0) $scatters[$i]['yaxis'] = $i+1; - if (floatval($c[$f]) < 1e38) array_push($scatters[$i]['data'], array($this->jst($c['ST']), floatval(round($c[$f],4)))); + if ($lastvisit && ($d['VISIT'] != $lastvisit)) { + if (array_key_exists($lastvisit, $vis_map)) { + $s = $vis_map[$lastvisit]; + // print_r(array($startvisit, $lastend, $s)); + array_push($data, array('data' => array( + array($this->jst($startvisit), 7, $this->jst($startvisit)), + array($this->jst($lastend), 7, $this->jst($startvisit)) + ), 'color' => 'purple', 'type' => 'visit_ns', 'status' => $s['VISIT'] . ': ' . $s['TITLE'], 'visit' => $s['VISIT'])); } + + $startvisit = null; } - $queued = 0; - $vis_map = array(); - foreach ($sched as $s) { - if ($s['SCHEDULED'] != '1' || $s['PROPOSALCODE'] == 'lb' || $s['PROPOSALCODE'] == 'nr') { - $vis_map[$s['VISIT']] = $s; - $queued++; - continue; - } + if (strpos($d['RUNSTATUS'], 'Successful') === false) + $info['DC_STOPPED']++; + + if ($d['ST'] && $d['EN']) { array_push($data, array('data' => array( - array($this->jst($s['ST']), 6, $this->jst($s['ST'])), - array($this->jst($s['EN']), 6, $this->jst($s['ST']))), 'color' => 'purple', 'type' => 'visit', 'status' => $s['VISIT'].': '.$s['TITLE'], 'visit' => $s['VISIT'])); - } + array($this->jst($d['ST']), 1, $this->jst($d['ST'])), + array($this->jst($d['EN']), 1, $this->jst($d['ST'])) + ), 'color' => 'green', 'id' => intval($d['ID']), 'type' => 'dc', 'visit' => $d['VISIT'], 'pid' => $d['PROTEINID'], 'status' => 'Protein: ' . $d['PROTEIN'])); - $lastend = null; - $lastvisit = null; - $startvisit = null; - foreach ($dc as $d) { - - if ($lastvisit && ($d['VISIT'] != $lastvisit)) { - if (array_key_exists($lastvisit, $vis_map)) { - $s = $vis_map[$lastvisit]; - // print_r(array($startvisit, $lastend, $s)); - array_push($data, array('data' => array( - array($this->jst($startvisit), 7, $this->jst($startvisit)), - array($this->jst($lastend), 7, $this->jst($startvisit))), 'color' => 'purple', 'type' => 'visit_ns', 'status' => $s['VISIT'].': '.$s['TITLE'], 'visit' => $s['VISIT'])); - } + $d['ENERGY'] = $d['WAVELENGTH'] ? (1.98644568e-25 / ($d['WAVELENGTH'] * 1e-10)) / 1.60217646e-19 : ''; - $startvisit = null; + foreach ($linecols as $i => $f) { + $lines[$i]['label'] = $f; + if ($i > 0) + $lines[$i]['yaxis'] = $i > 2 ? 3 : ($i > 0 ? 2 : 1); + array_push($lines[$i]['data'], array($this->jst($d['ST']), floatval(round($d[$f], 4)))); } + } - if (strpos($d['RUNSTATUS'], 'Successful') === false) $info['DC_STOPPED']++; - - if ($d['ST'] && $d['EN']) { - array_push($data, array('data' => array( - array($this->jst($d['ST']), 1, $this->jst($d['ST'])), - array($this->jst($d['EN']), 1, $this->jst($d['ST']))), 'color' => 'green', 'id' => intval($d['ID']), 'type' => 'dc', 'visit' => $d['VISIT'], 'pid' => $d['PROTEINID'], 'status' => 'Protein: '.$d['PROTEIN'])); + $lastvisit = $d['VISIT']; + if (!$startvisit) + $startvisit = $d['ST']; + $lastend = $d['EN'] ? $d['EN'] : $d['ST']; + } - $d['ENERGY'] = $d['WAVELENGTH'] ? (1.98644568e-25/($d['WAVELENGTH']*1e-10))/1.60217646e-19 : ''; + if (array_key_exists($lastvisit, $vis_map)) { + $s = $vis_map[$lastvisit]; + // print_r(array($startvisit, $lastend, $s)); + array_push($data, array('data' => array( + array($this->jst($startvisit), 7, $this->jst($startvisit)), + array($this->jst($lastend), 7, $this->jst($startvisit)) + ), 'color' => 'purple', 'type' => 'visit_ns', 'status' => $s['VISIT'] . ': ' . $s['TITLE'], 'visit' => $s['VISIT'])); + } - foreach ($linecols as $i => $f) { - $lines[$i]['label'] = $f; - if ($i > 0) $lines[$i]['yaxis'] = $i > 2 ? 3 : ($i > 0 ? 2 : 1); - array_push($lines[$i]['data'], array($this->jst($d['ST']), floatval(round($d[$f],4)))); - } - } + // return; + foreach ($robot as $r) { + array_push($data, array('data' => array( + array($this->jst($r['ST']), 2, $this->jst($r['ST'])), + array($this->jst($r['EN']), 2, $this->jst($r['ST'])) + ), 'color' => $r['STATUS'] != 'SUCCESS' ? 'purple' : 'blue', 'status' => ' ' . $r['ACTIONTYPE'] . ' (' . $r['STATUS'] . ')', 'type' => 'robot')); + } - $lastvisit = $d['VISIT']; - if (!$startvisit) $startvisit = $d['ST']; - $lastend = $d['EN'] ? $d['EN'] : $d['ST']; - } + foreach ($edge as $e) { + array_push($data, array('data' => array( + array($this->jst($e['ST']), 3, $this->jst($e['ST'])), + array($this->jst($e['EN']), 3, $this->jst($e['ST'])) + ), 'color' => 'orange', 'id' => $e['ID'], 'type' => 'ed', 'pid' => $d['PROTEINID'], 'status' => 'Protein: ' . $d['PROTEIN'])); + } - if (array_key_exists($lastvisit, $vis_map)) { - $s = $vis_map[$lastvisit]; - // print_r(array($startvisit, $lastend, $s)); - array_push($data, array('data' => array( - array($this->jst($startvisit), 7, $this->jst($startvisit)), - array($this->jst($lastend), 7, $this->jst($startvisit))), 'color' => 'purple', 'type' => 'visit_ns', 'status' => $s['VISIT'].': '.$s['TITLE'], 'visit' => $s['VISIT'])); - } - - // return; - foreach ($robot as $r) { - array_push($data, array('data' => array( - array($this->jst($r['ST']), 2, $this->jst($r['ST'])), - array($this->jst($r['EN']), 2, $this->jst($r['ST']))), 'color' => $r['STATUS'] != 'SUCCESS' ? 'purple' : 'blue', 'status' => ' ' . $r['ACTIONTYPE'] . ' (' . $r['STATUS'] . ')', 'type' => 'robot')); - } - - foreach ($edge as $e) { + foreach ($fl as $e) { + array_push($data, array('data' => array( + array($this->jst($e['ST']), 3, $this->jst($e['ST'])), + array($this->jst($e['EN']), 3, $this->jst($e['ST'])) + ), 'color' => 'red', 'id' => $e['ID'], 'type' => 'mca', 'pid' => $d['PROTEINID'], 'status' => 'Protein: ' . $d['PROTEIN'])); + } + + foreach ($faultl as $f) { + array_push($data, array('data' => array( + array($this->jst($f['ST']), 4, $this->jst($f['ST'])), + array($this->jst($f['EN']), 4, $this->jst($f['ST'])) + ), 'color' => 'grey', 'type' => 'fault', 'status' => ' Fault: ' . $f['TITLE'])); + } + + foreach ($ai as $d) { + if ($d['ST'] && $d['EN']) array_push($data, array('data' => array( - array($this->jst($e['ST']), 3, $this->jst($e['ST'])), - array($this->jst($e['EN']), 3, $this->jst($e['ST']))), 'color' => 'orange', 'id' => $e['ID'], 'type' => 'ed', 'pid' => $d['PROTEINID'], 'status' => 'Protein: '.$d['PROTEIN'])); - } + array($this->jst($d['ST']), 1, $this->jst($d['ST'])), + array($this->jst($d['EN']), 1, $this->jst($d['ST'])) + ), 'color' => '#93db70', 'id' => intval($d['ID']), 'type' => 'ai')); + } - foreach ($fl as $e) { + foreach ($cent as $c) { + if ($c['ST'] && $c['EN']) array_push($data, array('data' => array( - array($this->jst($e['ST']), 3, $this->jst($e['ST'])), - array($this->jst($e['EN']), 3, $this->jst($e['ST']))), 'color' => 'red', 'type' => 'mca', 'id' => $e['ID'], 'type' => 'mca', 'pid' => $d['PROTEINID'], 'status' => 'Protein: '.$d['PROTEIN'])); + array($this->jst($c['ST']), 1.5, $this->jst($c['ST'])), + array($this->jst($c['EN']), 1.5, $this->jst($c['ST'])) + ), 'color' => 'cyan', 'type' => 'cent')); + } + + // Beam status + $bs = $this->_get_archive('CS-CS-MSTAT-01:MODE', strtotime($info['ST']), strtotime($info['EN'])); + + + $lastv = 0; + $ex = 3600 * 1000; + $st = $this->jst($info['ST']); + + foreach ($bs as $i => $b) { + $v = $b[1] != 4; + $c = $b[0] * 1000; + + if (($v != $lastv) && $v) { + $st = $c; } - - foreach ($faultl as $f) { + + if ($lastv && ($v != $lastv)) { array_push($data, array('data' => array( - array($this->jst($f['ST']), 4, $this->jst($f['ST'])), - array($this->jst($f['EN']), 4, $this->jst($f['ST']))), 'color' => 'grey', 'type' => 'fault', 'status' => ' Fault: '.$f['TITLE'])); - } - - foreach ($ai as $d) { - if ($d['ST'] && $d['EN']) - array_push($data, array('data' => array( - array($this->jst($d['ST']), 1, $this->jst($d['ST'])), - array($this->jst($d['EN']), 1, $this->jst($d['ST']))), 'color' => '#93db70', 'id' => intval($d['ID']), 'type' => 'ai')); + array($st + $ex, 5, $st + $ex), + array($c + $ex, 5, $st + $ex) + ), 'color' => 'black', 'status' => ' Beam Dump', 'type' => 'nobeam')); } - foreach ($cent as $c) { - if ($c['ST'] && $c['EN']) - array_push($data, array('data' => array( - array($this->jst($c['ST']), 1.5, $this->jst($c['ST'])), - array($this->jst($c['EN']), 1.5, $this->jst($c['ST']))), 'color' => 'cyan', 'type' => 'cent')); - } - - // Beam status - //$bs = $this->_get_archive('SR-DI-DCCT-01:SIGNAL', strtotime($info['ST']), strtotime($info['EN']), 200); - $bs = $this->_get_archive('CS-CS-MSTAT-01:MODE', strtotime($info['ST']), strtotime($info['EN']), 2000); - - if (!$bs) $bs = array(); - - $lastv = 0; - $ex = 3600*1000; - $bd = False; - foreach ($bs as $i => $b) { - //$v = $b[1] < 5 ? 1 : 0; - $v = $b[1] != 4; - $c = $b[0]*1000; - - if (($v != $lastv) && $v) { - $bd = True; - $st = $c; - } - - if ($lastv && ($v != $lastv)) { - array_push($data, array('data' => array( - array($st+$ex, 5, $st+$ex), - array($c+$ex, 5, $st+$ex)), 'color' => 'black', 'status' => ' Beam Dump', 'type' => 'nobeam')); - $bd = False; - } - - $lastv = $v; - } - - - $first = $info['ST']; - $last = $info['EN']; - if (sizeof($dc)) { - $f = $dc[0]; - $l = end($dc); - if (strtotime($f['EN'] ? $f['EN'] : $f['ST']) > strtotime($info['EN'])) $last = $f['EN'] ? $f['EN'] : $f['ST']; - if (strtotime($l['ST']) < strtotime($info['ST'])) $first = $l['ST']; - $info['LAST'] = $l['ST']; - } else $info['LAST'] = ''; - - $info['start'] = $this->jst($first); - $info['end'] = $this->jst($last); - - $this->_output(array('info' => $info, 'data' => $data, 'lines' => $lines, 'scatters' => $scatters)); + $lastv = $v; } - - - function _pies() { - $args = array($this->proposalid); - $where = ' WHERE p.proposalid=:1'; - - if ($this->has_arg('visit')) { - $info = $this->_check_visit(); - $where .= ' AND s.sessionid=:'.(sizeof($args)+1); - array_push($args, $info['SID']); - } - - $dc = $this->db->pq("SELECT max(p.title) as title, TO_CHAR(MAX(dc.endtime), 'DD-MM-YYYY HH24:MI') as last, SUM(TIMESTAMPDIFF('SECOND', dc.starttime, dc.endtime))/3600 as dctime, GREATEST(TIMESTAMPDIFF('SECOND', min(s.startdate), min(dc.starttime))/3600,0) as sup, GREATEST(TIMESTAMPDIFF('SECOND', max(dc.endtime), max(s.enddate))/3600,0) as rem, s.visit_number as visit, TO_CHAR(min(s.startdate), 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(max(s.enddate), 'DD-MM-YYYY HH24:MI') as en, TIMESTAMPDIFF('SECOND', min(s.startdate), max(s.enddate))/3600 as len + + // in case visit ends with no beam + if ($lastv && $this->jst($info['EN']) > $st + $ex) { + array_push($data, array('data' => array( + array($st + $ex, 5, $st + $ex), + array($this->jst($info['EN']), 5, $st + $ex) + ), 'color' => 'black', 'status' => ' Beam Dump', 'type' => 'nobeam')); + } + + $first = $info['ST']; + $last = $info['EN']; + if (sizeof($dc)) { + $f = $dc[0]; + $l = end($dc); + if (strtotime($f['EN'] ? $f['EN'] : $f['ST']) > strtotime($info['EN'])) + $last = $f['EN'] ? $f['EN'] : $f['ST']; + if (strtotime($l['ST']) < strtotime($info['ST'])) + $first = $l['ST']; + $info['LAST'] = $l['ST']; + } else + $info['LAST'] = ''; + + $info['start'] = $this->jst($first); + $info['end'] = $this->jst($last); + + $this->_output(array('info' => $info, 'data' => $data, 'lines' => $lines, 'scatters' => $scatters)); + } + + + function _pies() + { + $args = array($this->proposalid); + $where = ' WHERE p.proposalid=:1'; + + if ($this->has_arg('visit')) { + $info = $this->_check_visit(); + $where .= ' AND s.sessionid=:' . (sizeof($args) + 1); + array_push($args, $info['SID']); + } + + $dc = $this->db->pq("SELECT max(p.title) as title, TO_CHAR(MAX(dc.endtime), 'DD-MM-YYYY HH24:MI') as last, SUM(TIMESTAMPDIFF('SECOND', dc.starttime, dc.endtime))/3600 as dctime, GREATEST(TIMESTAMPDIFF('SECOND', min(s.startdate), min(dc.starttime))/3600,0) as sup, GREATEST(TIMESTAMPDIFF('SECOND', max(dc.endtime), max(s.enddate))/3600,0) as rem, s.visit_number as visit, TO_CHAR(min(s.startdate), 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(max(s.enddate), 'DD-MM-YYYY HH24:MI') as en, TIMESTAMPDIFF('SECOND', min(s.startdate), max(s.enddate))/3600 as len FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN datacollectiongroup dcg on dcg.sessionid = s.sessionid INNER JOIN datacollection dc ON dc.datacollectiongroupid = dcg.datacollectiongroupid $where GROUP BY s.visit_number ORDER BY min(s.startdate) DESC", $args); - - $robot = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)))/3600 as dctime, s.visit_number as visit FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN robotaction r ON (r.blsessionid = s.sessionid) $where GROUP BY s.visit_number", $args); - $edge = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', e.starttime, e.endtime))/3600 as dctime, s.visit_number as visit FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN energyscan e ON (e.sessionid = s.sessionid) $where GROUP BY s.visit_number", $args); - - $ai = $this->db->pq("SELECT SUM(ai) as aitime, visit FROM ( + $robot = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', CAST(r.starttimestamp AS DATE), CAST(r.endtimestamp AS DATE)))/3600 as dctime, s.visit_number as visit FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN robotaction r ON (r.blsessionid = s.sessionid) $where GROUP BY s.visit_number", $args); + + $edge = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', e.starttime, e.endtime))/3600 as dctime, s.visit_number as visit FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) INNER JOIN energyscan e ON (e.sessionid = s.sessionid) $where GROUP BY s.visit_number", $args); + + $ai = $this->db->pq("SELECT SUM(ai) as aitime, visit FROM ( SELECT TIMESTAMPDIFF('SECOND', dc.endtime, max(sc.bltimestamp))/3600 as ai, s.visit_number as visit FROM datacollection dc INNER JOIN screening sc ON sc.datacollectionid = dc.datacollectionid INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid $where GROUP BY dc.datacollectionid, s.visit_number, dc.endtime ) inq GROUP BY visit", $args); - - $cent = $this->db->pq("SELECT SUM(cent) as centtime, visit FROM ( + + $cent = $this->db->pq("SELECT SUM(cent) as centtime, visit FROM ( SELECT TIMESTAMPDIFF('SECOND', CAST(r.endtimestamp AS DATE), min(dc.starttime))/3600 as cent, s.visit_number as visit FROM robotaction r INNER JOIN datacollection dc ON (r.blsampleid = dc.blsampleid AND r.endtimestamp < dc.starttime) @@ -397,112 +464,129 @@ function _pies() { INNER JOIN blsession s ON s.sessionid = dcg.sessionid INNER JOIN proposal p ON p.proposalid = s.proposalid $where GROUP BY r.endtimestamp, s.visit_number) inq WHERE cent < 0.25 GROUP BY visit", $args); - - $fault = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', f.beamtimelost_starttime, f.beamtimelost_endtime))/3600 as lost, s.visit_number as visit FROM bf_fault f INNER JOIN blsession s ON f.sessionid = s.sessionid INNER JOIN proposal p ON (p.proposalid = s.proposalid) $where GROUP BY s.visit_number", $args); - - foreach ($robot as $r) { - foreach ($dc as &$d) { - if ($r['VISIT'] == $d['VISIT']) $d['R'] = $r['DCTIME']; - } - } - foreach ($edge as $e) { - foreach ($dc as &$d) { - if ($e['VISIT'] == $d['VISIT']) $d['EDGE'] = $e['DCTIME']; - } + $fault = $this->db->pq("SELECT SUM(TIMESTAMPDIFF('SECOND', f.beamtimelost_starttime, f.beamtimelost_endtime))/3600 as lost, s.visit_number as visit FROM bf_fault f INNER JOIN blsession s ON f.sessionid = s.sessionid INNER JOIN proposal p ON (p.proposalid = s.proposalid) $where GROUP BY s.visit_number", $args); + + foreach ($robot as $r) { + foreach ($dc as &$d) { + if ($r['VISIT'] == $d['VISIT']) + $d['R'] = $r['DCTIME']; } + } - foreach ($ai as $a) { - foreach ($dc as &$d) { - if ($a['VISIT'] == $d['VISIT']) $d['AITIME'] = $a['AITIME'] ? $a['AITIME'] : 0; - } + foreach ($edge as $e) { + foreach ($dc as &$d) { + if ($e['VISIT'] == $d['VISIT']) + $d['EDGE'] = $e['DCTIME']; } + } - foreach ($cent as $a) { - foreach ($dc as &$d) { - if ($a['VISIT'] == $d['VISIT']) $d['CENTTIME'] = $a['CENTTIME'] ? $a['CENTTIME'] : 0; - } + foreach ($ai as $a) { + foreach ($dc as &$d) { + if ($a['VISIT'] == $d['VISIT']) + $d['AITIME'] = $a['AITIME'] ? $a['AITIME'] : 0; } - - foreach ($fault as $f) { - foreach ($dc as &$d) { - if ($f['VISIT'] == $d['VISIT']) $d['FAULT'] = $f['LOST'] ? $f['LOST'] : 0; - } + } + + foreach ($cent as $a) { + foreach ($dc as &$d) { + if ($a['VISIT'] == $d['VISIT']) + $d['CENTTIME'] = $a['CENTTIME'] ? $a['CENTTIME'] : 0; } - - $i = 0; + } + + foreach ($fault as $f) { foreach ($dc as &$d) { - if (!array_key_exists('R', $d)) $d['R'] = 0; - if (!array_key_exists('EDGE', $d)) $d['EDGE'] = 0; - if (!array_key_exists('AITIME', $d)) $d['AITIME'] = 0; - if (!array_key_exists('CENTTIME', $d)) $d['CENTTIME'] = 0; - if (!array_key_exists('FAULT', $d)) $d['FAULT'] = 0; - - // Beam status - $bs = $this->_get_archive('CS-CS-MSTAT-01:MODE', strtotime($d['ST']), strtotime($d['EN']), 2000); - if (!$bs) $bs = array(); - - $lastv = 0; - $ex = 3600*1000; - $bd = False; - $total_no_beam = 0; - foreach ($bs as $i => $b) { - //$v = $b[1] < 5 ? 1 : 0; - $v = $b[1] != 4; - $c = $b[0]*1000; - - if (($v != $lastv) && $v) { - $bd = True; - $st = $c; - } - - if ($lastv && ($v != $lastv)) { - $total_no_beam += ($c - $st) / 1000; - $bd = False; - } - - $lastv = $v; + if ($f['VISIT'] == $d['VISIT']) + $d['FAULT'] = $f['LOST'] ? $f['LOST'] : 0; + } + } + + $i = 0; + foreach ($dc as &$d) { + if (!array_key_exists('R', $d)) + $d['R'] = 0; + if (!array_key_exists('EDGE', $d)) + $d['EDGE'] = 0; + if (!array_key_exists('AITIME', $d)) + $d['AITIME'] = 0; + if (!array_key_exists('CENTTIME', $d)) + $d['CENTTIME'] = 0; + if (!array_key_exists('FAULT', $d)) + $d['FAULT'] = 0; + + // Beam status + $bs = $this->_get_archive('CS-CS-MSTAT-01:MODE', strtotime($d['ST']), strtotime($d['EN'])); + + $lastv = 0; + $ex = 3600 * 1000; + $st = $this->jst($d['ST']); + $total_no_beam = 0; + foreach ($bs as $i => $b) { + $v = $b[1] != 4; + $c = $b[0] * 1000; + + if (($v != $lastv) && $v) { + $st = $c; } - - $d['NOBEAM'] = $total_no_beam/3600; - - $d['T'] = max($d['LEN'] - $d['SUP'] - $d['DCTIME'] - $d['R'] - $d['REM'] - $d['EDGE'] - $d['AITIME'] - $d['CENTTIME'] - $d['FAULT'] - $d['NOBEAM'],0); - - foreach (array('SUP', 'DCTIME', 'LEN', 'R', 'REM', 'T', 'EDGE') as $nf) $d[$nf] = number_format($d[$nf], 2, '.', ''); - - $d['ID'] = $i; - - $i++; + + if ($lastv && ($v != $lastv)) { + $total_no_beam += ($c - $st) / 1000; + } + + $lastv = $v; } - - # Averages - $avgs = array(); - foreach (array('T', 'SUP', 'DCTIME', 'R', 'REM', 'EDGE', 'AITIME', 'CENTTIME', 'FAULT', 'NOBEAM') as $i => $col) { - $arr = array_map(function($d) use ($col) { return $d[$col]; }, $dc); - $avgs[$col] = sizeof($arr) ? array_sum($arr) / count($arr) : 0; + + // in case visit ends with no beam + if ($lastv && $this->jst($d['EN']) > ($ex + $st)) { + $total_no_beam += ($this->jst($d['EN']) - ($ex + $st)) / 1000; } - #foreach ($plot as $p) { - # $arr = array_map(function($i) { return $i[1]; }, $p); - # array_push($avgs, sizeof($arr) ? array_sum($arr) / count($arr) : 0); - #} - - if ($this->has_arg('visit')) $this->_output(sizeof($dc) ? $dc[0] : array()); - else $this->_output(array('VISITS' => $dc, 'AVERAGE' => $avgs)); + + $d['NOBEAM'] = $total_no_beam / 3600; + + $d['T'] = max($d['LEN'] - $d['SUP'] - $d['DCTIME'] - $d['R'] - $d['REM'] - $d['EDGE'] - $d['AITIME'] - $d['CENTTIME'] - $d['FAULT'] - $d['NOBEAM'], 0); + + foreach (array('SUP', 'DCTIME', 'LEN', 'R', 'REM', 'T', 'EDGE') as $nf) + $d[$nf] = number_format($d[$nf], 2, '.', ''); + + $d['ID'] = $i; + + $i++; } - - - function _hourlies() { - $args = array($this->proposalid); - $where = ''; - - if ($this->has_arg('visit')) { - $info = $this->_check_visit(); - $where .= ' AND s.sessionid=:'.(sizeof($args)+1); - array_push($args, $info['SID']); - } - - # Data Collections / Hour - $dch_tmp = $this->db->pq("SELECT AVG(datacollections) as dcs, dh as hour FROM ( + + # Averages + $avgs = array(); + foreach (array('T', 'SUP', 'DCTIME', 'R', 'REM', 'EDGE', 'AITIME', 'CENTTIME', 'FAULT', 'NOBEAM') as $i => $col) { + $arr = array_map(function ($d) use ($col) { + return $d[$col]; + }, $dc); + $avgs[$col] = sizeof($arr) ? array_sum($arr) / count($arr) : 0; + } + #foreach ($plot as $p) { + # $arr = array_map(function($i) { return $i[1]; }, $p); + # array_push($avgs, sizeof($arr) ? array_sum($arr) / count($arr) : 0); + #} + + if ($this->has_arg('visit')) + $this->_output(sizeof($dc) ? $dc[0] : array()); + else + $this->_output(array('VISITS' => $dc, 'AVERAGE' => $avgs)); + } + + + function _hourlies() + { + $args = array($this->proposalid); + $where = ''; + + if ($this->has_arg('visit')) { + $info = $this->_check_visit(); + $where .= ' AND s.sessionid=:' . (sizeof($args) + 1); + array_push($args, $info['SID']); + } + + # Data Collections / Hour + $dch_tmp = $this->db->pq("SELECT AVG(datacollections) as dcs, dh as hour FROM ( SELECT count(dc.datacollectionid) as datacollections, TO_CHAR(dc.starttime, 'HH24') as dh FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid @@ -512,14 +596,14 @@ function _hourlies() { GROUP BY TO_CHAR(dc.starttime, 'DDHH24'), s.visit_number ) inq GROUP BY dh ORDER BY hour ", $args); - $dch = array(); - foreach ($dch_tmp as $d) { - array_push($dch, array($d['HOUR'], $d['DCS'])); - } - - - # Samples Loaded / Hour - $slh_tmp = $this->db->pq("SELECT AVG(samples) as SLH, dh as hour FROM ( + $dch = array(); + foreach ($dch_tmp as $d) { + array_push($dch, array($d['HOUR'], $d['DCS'])); + } + + + # Samples Loaded / Hour + $slh_tmp = $this->db->pq("SELECT AVG(samples) as SLH, dh as hour FROM ( SELECT count(r.robotactionid) as samples, TO_CHAR(r.starttimestamp, 'HH24') as dh FROM robotaction r INNER JOIN blsession s ON s.sessionid = r.blsessionid @@ -528,236 +612,252 @@ function _hourlies() { GROUP BY TO_CHAR(r.starttimestamp, 'DDHH24'), s.visit_number ) inq GROUP BY dh ORDER BY hour ", $args); - $slh = array(); - foreach ($slh_tmp as $d) { - array_push($slh, array($d['HOUR'], $d['SLH'])); - } - - $this->_output(array('samples' => $slh, 'dcs' => $dch)); + $slh = array(); + foreach ($slh_tmp as $d) { + array_push($slh, array($d['HOUR'], $d['SLH'])); } - - function _error_log() { - $info = $this->_check_visit(); + $this->_output(array('samples' => $slh, 'dcs' => $dch)); + } + - $dc_tot = $this->db->pq("SELECT dcg.experimenttype, count(dc.datacollectionid) as total + function _error_log() + { + $info = $this->_check_visit(); + + $dc_tot = $this->db->pq("SELECT dcg.experimenttype, count(dc.datacollectionid) as total FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dc.datacollectiongroupid = dcg.datacollectiongroupid WHERE dcg.sessionid=:1 GROUP BY dcg.experimenttype", array($info['SID'])); - - $totals = array(); - foreach($dc_tot as $tot) { - $totals[$tot['EXPERIMENTTYPE']] = intval($tot["TOTAL"]); - } - $dcs = $this->db->pq("SELECT dc.datacollectionid, dcg.experimenttype, dc.starttime, dc.endtime, dc.filetemplate, dc.imagedirectory, dc.runstatus, dca.filefullpath as logfile + $totals = array(); + foreach ($dc_tot as $tot) { + $totals[$tot['EXPERIMENTTYPE']] = intval($tot["TOTAL"]); + } + + $dcs = $this->db->pq("SELECT dc.datacollectionid, dcg.experimenttype, dc.starttime, dc.endtime, dc.filetemplate, dc.imagedirectory, dc.runstatus, dca.filefullpath as logfile FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dc.datacollectiongroupid = dcg.datacollectiongroupid LEFT OUTER JOIN blsample sam ON dc.blsampleid = sam.blsampleid LEFT OUTER JOIN datacollectionfileattachment dca ON dca.datacollectionid = dc.datacollectionid AND dca.filetype = 'log' WHERE dcg.sessionid=:1 AND dc.runstatus NOT LIKE '%success%'", array($info['SID'])); - $dc_types = array(); - foreach ($dcs as $dc) { - if (!array_key_exists($dc['EXPERIMENTTYPE'], $dc_types)) { - $dc_types[$dc['EXPERIMENTTYPE']] = array( - 'type' => $dc['EXPERIMENTTYPE'], - 'total' => array_key_exists($dc['EXPERIMENTTYPE'], $totals) ? $totals[$dc['EXPERIMENTTYPE']] : 0, - 'failed' => 0, - 'aborted' => 0, - 'messages' => array() - ); - } - - - if (preg_match('/aborted/i', $dc['RUNSTATUS'])) { - $dc_types[$dc['EXPERIMENTTYPE']]['aborted']++; - } else { - $dc_types[$dc['EXPERIMENTTYPE']]['failed']++; - if ($dc['LOGFILE']) { - $lines = file($dc['LOGFILE']); - $last = rtrim(end($lines)); + $dc_types = array(); + foreach ($dcs as $dc) { + if (!array_key_exists($dc['EXPERIMENTTYPE'], $dc_types)) { + $dc_types[$dc['EXPERIMENTTYPE']] = array( + 'type' => $dc['EXPERIMENTTYPE'], + 'total' => array_key_exists($dc['EXPERIMENTTYPE'], $totals) ? $totals[$dc['EXPERIMENTTYPE']] : 0, + 'failed' => 0, + 'aborted' => 0, + 'messages' => array() + ); + } - if (!array_key_exists($last, $dc_types[$dc['EXPERIMENTTYPE']]['messages'])) { - $dc_types[$dc['EXPERIMENTTYPE']]['messages'][$last] = array( - 'count' => 0, - 'message' => $last, - ); - } - $dc_types[$dc['EXPERIMENTTYPE']]['messages'][$last]['count']++; + if (preg_match('/aborted/i', $dc['RUNSTATUS'])) { + $dc_types[$dc['EXPERIMENTTYPE']]['aborted']++; + } else { + $dc_types[$dc['EXPERIMENTTYPE']]['failed']++; + if ($dc['LOGFILE']) { + $lines = file($dc['LOGFILE']); + $last = rtrim(end($lines)); + + if (!array_key_exists($last, $dc_types[$dc['EXPERIMENTTYPE']]['messages'])) { + $dc_types[$dc['EXPERIMENTTYPE']]['messages'][$last] = array( + 'count' => 0, + 'message' => $last, + ); } - } - } - foreach ($dc_types as &$ty) { - $ty['messages'] = array_values($ty['messages']); + $dc_types[$dc['EXPERIMENTTYPE']]['messages'][$last]['count']++; + } } - - $this->_output(array_values($dc_types)); } - - function _ehc_log() { - $info = $this->_check_visit(); - - $en = strtotime($info['EN']); - $ehc_tmp = $this->_get_remote_xml('https://rdb.pri.diamond.ac.uk/php/elog/cs_logwscontentinfo.php?startdate='.date('d/m/Y', $en)); - if (!$ehc_tmp) $ehc_tmp = array(); - - $ehcs = array(); - foreach ($ehc_tmp as $e) { - //if (strpos($e->title, 'shift') !== False) - array_push($ehcs, $e); - } - - $this->_output($ehcs); - } - - - function _callouts() { - $info = $this->_check_visit(); - - $st = strtotime($info['ST']); - $en = strtotime($info['EN']); - - $bls = array('i02' => 'BLI02', 'i03' => 'BLI03', 'i04' => 'BLI04', 'i04-1' => 'BLI04J', 'i24' => 'BLI24', 'i23' => 'BLI23', - 'i11-1' => 'BLI11', - 'b21' => 'BLB21', - 'i12' => 'BLI12', 'i13' => 'BLI13', 'i13-1' => 'BLI13J', - ); - $calls = $this->_get_remote_xml('https://rdb.pri.diamond.ac.uk/php/elog/cs_logwscalloutinfo.php?startdate='.date('d/m/Y', $st).'&enddate='.date('d/m/Y', $en).'selgroupid='.$bls[$info['BL']]); - if (!$calls) $calls = array(); - - $this->_output($calls); + foreach ($dc_types as &$ty) { + $ty['messages'] = array_values($ty['messages']); } - - - - function _check_visit() { - if (!$this->has_arg('visit')) $this->_error('No visit specified'); - - $args = array($this->arg('visit')); - $where = "WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1"; - - if (!$this->staff) { - if (!$this->has_arg('prop')) $this->_error('No proposal selected', 'You need to select a proposal before viewing this page'); - - $where .= ' AND p.proposalid LIKE :2'; - array_push($args, $this->proposalid); - } - - $info = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename as bl, s.sessionid as sid, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en, TIMESTAMPDIFF('HOUR', s.startdate, s.enddate) as len FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) $where", $args); - - if (!sizeof($info)) { - $this->_error('No such visit'); - } else return $info[0]; + $this->_output(array_values($dc_types)); + } + + + function _ehc_log() + { + $info = $this->_check_visit(); + + $en = strtotime($info['EN']); + $ehc_tmp = $this->_get_remote_xml('https://rdb.pri.diamond.ac.uk/php/elog/cs_logwscontentinfo.php?startdate=' . date('d/m/Y', $en)); + if (!$ehc_tmp) + $ehc_tmp = array(); + + $ehcs = array(); + foreach ($ehc_tmp as $e) { + //if (strpos($e->title, 'shift') !== False) + array_push($ehcs, $e); } - - - - // Return xml from external link without using url_fopen - function _get_remote_xml($url) { - libxml_use_internal_errors(true); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $xml = curl_exec($ch); - curl_close($ch); - - return simplexml_load_string($xml); + + $this->_output($ehcs); + } + + + function _callouts() + { + $info = $this->_check_visit(); + + $st = strtotime($info['ST']); + $en = strtotime($info['EN']); + + $bls = array( + 'i02' => 'BLI02', 'i03' => 'BLI03', 'i04' => 'BLI04', 'i04-1' => 'BLI04J', 'i24' => 'BLI24', 'i23' => 'BLI23', + 'i11-1' => 'BLI11', + 'b21' => 'BLB21', + 'i12' => 'BLI12', 'i13' => 'BLI13', 'i13-1' => 'BLI13J', + ); + $calls = $this->_get_remote_xml('https://rdb.pri.diamond.ac.uk/php/elog/cs_logwscalloutinfo.php?startdate=' . date('d/m/Y', $st) . '&enddate=' . date('d/m/Y', $en) . 'selgroupid=' . $bls[$info['BL']]); + if (!$calls) + $calls = array(); + + $this->_output($calls); + } + + + + + function _check_visit() + { + if (!$this->has_arg('visit')) + $this->_error('No visit specified'); + + $args = array($this->arg('visit')); + $where = "WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1"; + + if (!$this->staff) { + if (!$this->has_arg('prop')) + $this->_error('No proposal selected', 'You need to select a proposal before viewing this page'); + + $where .= ' AND p.proposalid LIKE :2'; + array_push($args, $this->proposalid); } + $info = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename as bl, s.sessionid as sid, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en, TIMESTAMPDIFF('HOUR', s.startdate, s.enddate) as len FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) $where", $args); + if (!sizeof($info)) { + $this->_error('No such visit'); + } else + return $info[0]; + } - // BAG Overview Stats - function _overview() { - $beamlines = $this->_get_beamlines_from_type($this->ty); - $bls = implode("', '", $beamlines); - $where = " AND p.proposalcode NOT IN ('cm') AND s.beamlinename in ('$bls')"; - $args = array(); + // Return xml from external link without using url_fopen + function _get_remote_xml($url) + { + libxml_use_internal_errors(true); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $xml = curl_exec($ch); + curl_close($ch); - if (!$this->user->has('all_prop_stats')) { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - $where .= ' AND p.proposalid = :'.(sizeof($args)+1); - array_push($args, $this->proposalid); - } + return simplexml_load_string($xml); + } - if ($this->has_arg('ty')) { - if ($this->arg('ty') == 'yearly') $where .= " AND TIMESTAMPDIFF('MONTH', s.startdate, CURRENT_TIMESTAMP) <= 12"; - if ($this->arg('ty') == 'monthly') $where .= " AND TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) <= 28"; - if ($this->arg('ty') == 'weekly') $where .= " AND TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) <= 7"; - } - if ($this->has_arg('runid')) { - $where .= " AND vr.runid=:".(sizeof($args)+1); - array_push($args, $this->arg('runid')); - } - if ($this->has_arg('scheduled')) { - $where .= " AND s.scheduled=1"; - } - if ($this->has_arg('proposalcode')) { - $where .= " AND p.proposalcode=:".(sizeof($args)+1); - array_push($args, $this->arg('proposalcode')); - } + // BAG Overview Stats + function _overview() + { + $beamlines = $this->_get_beamlines_from_type($this->ty); + $bls = implode("', '", $beamlines); - if ($this->has_arg('proposal')) { - $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :".(sizeof($args)+1); - array_push($args, $this->arg('proposal')); - } + $where = " AND p.proposalcode NOT IN ('cm') AND s.beamlinename in ('$bls')"; + $args = array(); - if ($this->has_arg('bl')) { - $where .= " AND s.beamlinename=:".(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } + if (!$this->user->hasPermission('all_prop_stats')) { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + $where .= ' AND p.proposalid = :' . (sizeof($args) + 1); + array_push($args, $this->proposalid); + } - if ($this->has_arg('s')) { - $st = sizeof($args) + 1; - $where .= " AND ( CONCAT(p.proposalcode, p.proposalnumber) LIKE lower(CONCAT(CONCAT('%',:".$st."), '%')) )"; - array_push($args, $this->arg('s')); - } + if ($this->has_arg('ty')) { + if ($this->arg('ty') == 'yearly') + $where .= " AND TIMESTAMPDIFF('MONTH', s.startdate, CURRENT_TIMESTAMP) <= 12"; + if ($this->arg('ty') == 'monthly') + $where .= " AND TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) <= 28"; + if ($this->arg('ty') == 'weekly') + $where .= " AND TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) <= 7"; + } - $match = 'PROP'; - if ($this->has_arg('group_by')) { - if ($this->arg('group_by') == 'run') { - $group = 'GROUP BY run'; - $match = 'RUN'; - } + if ($this->has_arg('runid')) { + $where .= " AND vr.runid=:" . (sizeof($args) + 1); + array_push($args, $this->arg('runid')); + } - if ($this->arg('group_by') == 'beamline') { - $group = 'GROUP BY beamlinename'; - $match = 'BEAMLINENAME'; - } + if ($this->has_arg('scheduled')) { + $where .= " AND s.scheduled=1"; + } - if ($this->arg('group_by') == 'type') { - $group = 'GROUP BY typename'; - $match = 'TYPENAME'; - } + if ($this->has_arg('proposalcode')) { + $where .= " AND p.proposalcode=:" . (sizeof($args) + 1); + array_push($args, $this->arg('proposalcode')); + } - if ($this->arg('group_by') == 'total') { - $group = ''; - $match = 'TOTAL'; - } + if ($this->has_arg('proposal')) { + $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :" . (sizeof($args) + 1); + array_push($args, $this->arg('proposal')); + } - if ($this->arg('group_by') == 'visit') { - $group = 'GROUP BY visit'; - $match = 'VISIT'; + if ($this->has_arg('bl')) { + $where .= " AND s.beamlinename=:" . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } - $where .= ' AND p.proposalid = :'.(sizeof($args)+1); - array_push($args, $this->proposalid); - } + if ($this->has_arg('s')) { + $st = sizeof($args) + 1; + $where .= " AND ( CONCAT(p.proposalcode, p.proposalnumber) LIKE lower(CONCAT(CONCAT('%',:" . $st . "), '%')) )"; + array_push($args, $this->arg('s')); + } - } else $group = 'GROUP BY prop'; + $match = 'PROP'; + if ($this->has_arg('group_by')) { + if ($this->arg('group_by') == 'run') { + $group = 'GROUP BY run'; + $match = 'RUN'; + } + + if ($this->arg('group_by') == 'beamline') { + $group = 'GROUP BY beamlinename'; + $match = 'BEAMLINENAME'; + } + + if ($this->arg('group_by') == 'type') { + $group = 'GROUP BY typename'; + $match = 'TYPENAME'; + } + + if ($this->arg('group_by') == 'total') { + $group = ''; + $match = 'TOTAL'; + } + + if ($this->arg('group_by') == 'visit') { + $group = 'GROUP BY visit'; + $match = 'VISIT'; + + $where .= ' AND p.proposalid = :' . (sizeof($args) + 1); + array_push($args, $this->proposalid); + } + } else + $group = 'GROUP BY prop'; - $dc = $this->db->pq(" + $dc = $this->db->pq(" SELECT COUNT(visit_number) as visits, visit, prop, SUM(rem) as rem, SUM(len) as len, ((SUM(len) - SUM(rem)) / SUM(len))*100 as used, beamlinename, 1 as total, typename, run, runid, startdate FROM ( SELECT GREATEST(TIMESTAMPDIFF('SECOND', MAX(dc.endtime), MAX(s.enddate))/3600,0) as rem, TIMESTAMPDIFF('SECOND', MAX(s.startdate), MAX(s.enddate))/3600 as len, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.visit_number, s.beamlinename, IF(st.typename IS NOT NULL, st.typename, 'Normal') as typename, vr.run, vr.runid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, s.startdate FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) @@ -772,7 +872,7 @@ function _overview() { ORDER BY startdate DESC,runid DESC", $args); - $dch = $this->db->pq("SELECT MAX(datacollections) as mdch, AVG(datacollections) as dch, SUM(datacollections) as dc, MAX(screenings) as msch, AVG(screenings) as sch, SUM(screenings) as sc, visit, prop, beamlinename, 1 as total, typename, run, runid, sum(multiaxis) as mx + $dch = $this->db->pq("SELECT MAX(datacollections) as mdch, AVG(datacollections) as dch, SUM(datacollections) as dc, MAX(screenings) as msch, AVG(screenings) as sch, SUM(screenings) as sc, visit, prop, beamlinename, 1 as total, typename, run, runid, sum(multiaxis) as mx FROM ( SELECT SUM(IF(dc.axisrange > 0 AND dc.overlap = 0, 1, 0)) as datacollections, SUM(IF(dc.overlap != 0, 1, 0)) as screenings, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename, IF(st.typename IS NOT NULL, st.typename, 'Normal') as typename, vr.run, vr.runid, SUM(IF(dc.axisrange > 0 AND dc.overlap = 0 AND ((dc.kappastart is not NULL AND dc.kappastart != 0) OR (dc.chistart is not NULL AND dc.chistart != 0) OR (dc.phistart is not NULL AND dc.phistart != 0)), 1, 0)) as multiaxis, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit @@ -787,8 +887,8 @@ function _overview() { ) inq $group ORDER BY runid DESC", $args); - - $slh = $this->db->pq("SELECT MAX(samples) as mslh, AVG(samples) as slh, SUM(samples) as sl, visit, prop, beamlinename, 1 as total, typename, run, runid FROM ( + + $slh = $this->db->pq("SELECT MAX(samples) as mslh, AVG(samples) as slh, SUM(samples) as sl, visit, prop, beamlinename, 1 as total, typename, run, runid FROM ( SELECT count(r.robotactionid) as samples, CONCAT(p.proposalcode, p.proposalnumber) as prop, s.beamlinename, IF(st.typename IS NOT NULL, st.typename, 'Normal') as typename, vr.run, vr.runid, CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit FROM robotaction r INNER JOIN blsession s ON s.sessionid = r.blsessionid @@ -802,80 +902,84 @@ function _overview() { ORDER BY runid DESC", $args); - foreach ($dc as &$d) { - foreach (array('DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MSLH', 'SLH', 'SL', 'MX') as $k) - if (!array_key_exists($k, $d)) $d[$k] = 0; + foreach ($dc as &$d) { + foreach (array('DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MSLH', 'SLH', 'SL', 'MX') as $k) + if (!array_key_exists($k, $d)) + $d[$k] = 0; - foreach ($dch as $dh) - if ($dh[$match] == $d[$match]) - foreach (array('DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MX') as $k) - $d[$k] = $dh[$k]; + foreach ($dch as $dh) + if ($dh[$match] == $d[$match]) + foreach (array('DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MX') as $k) + $d[$k] = $dh[$k]; - foreach ($slh as $sh) - if ($sh[$match] == $d[$match]) - foreach (array('SLH', 'MSLH', 'SL') as $k) - $d[$k] = $sh[$k]; + foreach ($slh as $sh) + if ($sh[$match] == $d[$match]) + foreach (array('SLH', 'MSLH', 'SL') as $k) + $d[$k] = $sh[$k]; - foreach(array('USED', 'REM', 'LEN', 'DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MSLH', 'SLH', 'SL', 'MX') as $t) if ($d[$t]) $d[$t] = round(floatval($d[$t]), 1); + foreach (array('USED', 'REM', 'LEN', 'DCH', 'MDCH', 'DC', 'SCH', 'MSCH', 'SC', 'MSLH', 'SLH', 'SL', 'MX') as $t) + if ($d[$t]) + $d[$t] = round(floatval($d[$t]), 1); + } + + if ($this->has_arg('download')) { + $data = array(); + array_push($data, array_keys($dc[0])); + foreach ($dc as $d) { + array_push($data, array_values($d)); } + $group = $this->has_arg('group_by') ? $this->arg('group_by') : 'prop'; + $run = $this->has_arg('runid') ? ('_runid_' . $this->arg('runid')) : ''; + $bl = $this->has_arg('bl') ? ('_' . $this->arg('bl')) : ''; + $prop = $this->has_arg('proposal') ? ('_' . $this->arg('proposal')) : ''; - if ($this->has_arg('download')) { - $data = array(); - array_push($data, array_keys($dc[0])); - foreach ($dc as $d) { - array_push($data, array_values($d)); - } - $group = $this->has_arg('group_by') ? $this->arg('group_by') : 'prop'; - $run = $this->has_arg('runid') ? ('_runid_'.$this->arg('runid')) : ''; - $bl = $this->has_arg('bl') ? ('_'.$this->arg('bl')) : ''; - $prop = $this->has_arg('proposal') ? ('_'.$this->arg('proposal')) : ''; + $this->_write_csv($data, 'groupby_' . $group . $bl . $prop . $run); + } else + $this->_output($dc); + } - $this->_write_csv($data, 'groupby_'.$group.$bl.$prop.$run); - } else $this->_output($dc); - } - - - - // Histogram of beamline parameters - function _parameter_histogram() { - $beamlines = $this->_get_beamlines_from_type($this->ty); - $bls = implode('\', \'', $beamlines); - - $types = array( - 'energy' => array('unit' => 'eV', 'st' => 5000, 'en' => 25000, 'bin_size' => 200, 'col' => '(1.98644568e-25/(dc.wavelength*1e-10))/1.60217646e-19', 'count' => 'dc.wavelength'), - 'beamsizex' => array('unit' => 'um', 'st' => 0, 'en' => 150, 'bin_size' => 5, 'col' => 'dc.beamsizeatsamplex*1000', 'count' => 'dc.beamsizeatsamplex'), - 'beamsizey' => array('unit' => 'um', 'st' => 0, 'en' => 150, 'bin_size' => 5, 'col' => 'dc.beamsizeatsampley*1000', 'count' => 'dc.beamsizeatsampley'), - 'exposuretime' => array('unit' => 'ms', 'st' => 0, 'en' => 5000, 'bin_size' => 50, 'col' => 'dc.exposuretime*1000', 'count' => 'dc.exposuretime'), - ); - - $k = 'energy'; - $t = $types[$k]; - if ($this->has_arg('ty')) { - if (array_key_exists($this->arg('ty'), $types)) { - $k = $this->arg('ty'); - $t = $types[$k]; - } - } - $where = ''; - $args = array(); + // Histogram of beamline parameters + function _parameter_histogram() + { + $beamlines = $this->_get_beamlines_from_type($this->ty); + $bls = implode('\', \'', $beamlines); - if ($this->has_arg('bl')) { - $where .= ' AND s.beamlinename=:'.(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } + $types = array( + 'energy' => array('unit' => 'eV', 'bin_size' => 200, 'col' => '(1.98644568e-25/(dc.wavelength*1e-10))/1.60217646e-19', 'count' => 'dc.wavelength'), + 'beamsizex' => array('unit' => 'um', 'bin_size' => 5, 'col' => 'dc.beamsizeatsamplex*1000', 'count' => 'dc.beamsizeatsamplex'), + 'beamsizey' => array('unit' => 'um', 'bin_size' => 5, 'col' => 'dc.beamsizeatsampley*1000', 'count' => 'dc.beamsizeatsampley'), + 'exposuretime' => array('unit' => 'ms', 'bin_size' => 5, 'col' => 'dc.exposuretime*1000', 'count' => 'dc.exposuretime'), + ); - if ($this->has_arg('runid')) { - $where .= ' AND vr.runid=:'.(sizeof($args)+1); - array_push($args, $this->arg('runid')); + $k = 'energy'; + $t = $types[$k]; + if ($this->has_arg('ty')) { + if (array_key_exists($this->arg('ty'), $types)) { + $k = $this->arg('ty'); + $t = $types[$k]; } + } - $col = $t['col']; - $ct = $t['count']; - $bs = $t['bin_size']; + $where = ''; + $args = array(); + + if ($this->has_arg('bl')) { + $where .= ' AND s.beamlinename=:' . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } - $hist = $this->db->pq("SELECT ($col div $bs) * $bs as x, count($ct) as y, s.beamlinename + if ($this->has_arg('runid')) { + $where .= ' AND vr.runid=:' . (sizeof($args) + 1); + array_push($args, $this->arg('runid')); + } + + $col = $t['col']; + $ct = $t['count']; + $binSize = $t['bin_size']; + + $hist = $this->db->pq("SELECT ($col div $binSize) * $binSize as x, count($ct) as y, s.beamlinename FROM datacollection dc INNER JOIN datacollectiongroup dcg ON dcg.datacollectiongroupid = dc.datacollectiongroupid INNER JOIN blsession s ON s.sessionid = dcg.sessionid @@ -885,124 +989,137 @@ function _parameter_histogram() { GROUP BY s.beamlinename,x ORDER BY s.beamlinename", $args); - $bls = array(); - foreach ($hist as $h) $bls[$h['BEAMLINENAME']] = 1; - - $data = array(); - foreach ($bls as $bl => $y) { - $ha = array(); - foreach ($hist as &$h) { - if ($h['BEAMLINENAME'] != $bl) continue; - $ha[$h['X']] = floatval($h['Y']); - } - - $gram = array(); - for($bin = $t['st']; $bin <= $t['en']; $bin += $t['bin_size']) { - $gram[$bin] = array_key_exists($bin, $ha) ? $ha[$bin] : 0; - } + $min = null; + $max = null; + $bls = array(); + foreach ($hist as $h) { + $bls[$h['BEAMLINENAME']] = 1; + if (is_null($max) || $h['X'] > $max) $max = $h['X']; + if (is_null($min) || $h['X'] < $min) $min = $h['X']; + } + $min = is_null($min) ? 0 : intval(floor($min/$binSize) * $binSize); // make min align with bin size, or 0 for no data - $lab = ucfirst($k).' ('.$t['unit'].')'; - if (!$this->has_arg('bl')) $lab = $bl.': '.$lab; + $data = array(); + foreach ($bls as $bl => $y) { + $ha = array(); + foreach ($hist as &$h) { + if ($h['BEAMLINENAME'] != $bl) + continue; + $ha[$h['X']] = floatval($h['Y']); + } - array_push($data, array('label' => $lab, 'data' => $gram)); + $gram = array(); + for ($bin = $min; $bin <= $max; $bin += $binSize) { + $gram[$bin] = array_key_exists($bin, $ha) ? $ha[$bin] : 0; } - $this->_output(array('histograms' => $data)); + $lab = ucfirst($k) . ' (' . $t['unit'] . ')'; + if (!$this->has_arg('bl')) + $lab = $bl . ': ' . $lab; + + array_push($data, array('label' => $lab, 'data' => $gram)); } + $this->_output(array('histograms' => $data)); + } + - // Return a list of runs - function _runs() { - $runs = $this->db->pq("SELECT runid,run + // Return a list of runs + function _runs() + { + $runs = $this->db->pq("SELECT runid,run FROM v_run WHERE startdate < CURRENT_TIMESTAMP ORDER BY startdate DESC"); - $this->_output($runs); - } + $this->_output($runs); + } - function _write_csv($data, $filename) { - header('Content-Type:application/csv'); - header("Content-Disposition:attachment;filename=$filename.csv"); - foreach($data as $d) { - print implode(',', $d)."\n"; - } + function _write_csv($data, $filename) + { + header('Content-Type:application/csv'); + header("Content-Disposition:attachment;filename=$filename.csv"); + foreach ($data as $d) { + print implode(',', $d) . "\n"; } + } - function _dewars_breakdown() { - $beamlines = $this->_get_beamlines_from_type($this->ty); + function _dewars_breakdown() + { + $beamlines = $this->_get_beamlines_from_type($this->ty); - $bls = implode("', '", $beamlines); + $bls = implode("', '", $beamlines); - // $where = " AND p.proposalcode NOT IN ('cm') AND ses.beamlinename in ('$bls')"; - $where = " AND p.proposalcode NOT IN ('cm')"; - $having = ''; - $args = array(); + // $where = " AND p.proposalcode NOT IN ('cm') AND ses.beamlinename in ('$bls')"; + $where = " AND p.proposalcode NOT IN ('cm')"; + $having = ''; + $args = array(); - if (!$this->user->has('all_prop_stats')) { - if (!$this->has_arg('prop')) $this->_error('No proposal specified'); - $where .= ' AND p.proposalid = :'.(sizeof($args)+1); - array_push($args, $this->proposalid); - } + if (!$this->user->hasPermission('all_prop_stats')) { + if (!$this->has_arg('prop')) + $this->_error('No proposal specified'); + $where .= ' AND p.proposalid = :' . (sizeof($args) + 1); + array_push($args, $this->proposalid); + } - if ($this->has_arg('data')) { - $having = 'HAVING count(dc.datacollectionid) > 0'; - } + if ($this->has_arg('data')) { + $having = 'HAVING count(dc.datacollectionid) > 0'; + } - if ($this->has_arg('history')) { - $having = 'HAVING count(th.dewartransporthistoryid) > 1'; - } + if ($this->has_arg('history')) { + $having = 'HAVING count(th.dewartransporthistoryid) > 1'; + } - if ($this->has_arg('scheduled')) { - $where .= " AND ses.scheduled=1"; - } + if ($this->has_arg('scheduled')) { + $where .= " AND ses.scheduled=1"; + } - if ($this->has_arg('proposalcode')) { - $where .= " AND p.proposalcode=:".(sizeof($args)+1); - array_push($args, $this->arg('proposalcode')); - } + if ($this->has_arg('proposalcode')) { + $where .= " AND p.proposalcode=:" . (sizeof($args) + 1); + array_push($args, $this->arg('proposalcode')); + } - if ($this->has_arg('proposal')) { - $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :".(sizeof($args)+1); - array_push($args, $this->arg('proposal')); - } + if ($this->has_arg('proposal')) { + $where .= " AND CONCAT(p.proposalcode,p.proposalnumber) LIKE :" . (sizeof($args) + 1); + array_push($args, $this->arg('proposal')); + } - if ($this->has_arg('bl')) { - $where .= " AND ses.beamlinename=:".(sizeof($args)+1); - array_push($args, $this->arg('bl')); - } + if ($this->has_arg('bl')) { + $where .= " AND ses.beamlinename=:" . (sizeof($args) + 1); + array_push($args, $this->arg('bl')); + } - $match = 'PROP'; - $group = 'GROUP BY prop'; - if ($this->has_arg('group_by')) { + $match = 'PROP'; + $group = 'GROUP BY prop'; + if ($this->has_arg('group_by')) { - if ($this->arg('group_by') == 'year') { - $group = 'GROUP BY year'; - $match = 'YEAR'; - } + if ($this->arg('group_by') == 'year') { + $group = 'GROUP BY year'; + $match = 'YEAR'; + } - if ($this->arg('group_by') == 'beamline') { - $group = 'GROUP BY beamlinename'; - $match = 'BEAMLINENAME'; - } + if ($this->arg('group_by') == 'beamline') { + $group = 'GROUP BY beamlinename'; + $match = 'BEAMLINENAME'; + } - if ($this->arg('group_by') == 'country') { - $group = 'GROUP BY country'; - $match = 'COUNTRY'; - } + if ($this->arg('group_by') == 'country') { + $group = 'GROUP BY country'; + $match = 'COUNTRY'; + } - if ($this->arg('group_by') == 'total') { - $group = ''; - $match = 'TOTAL'; - } + if ($this->arg('group_by') == 'total') { + $group = ''; + $match = 'TOTAL'; } + } - $dewars = $this->db->pq(" + $dewars = $this->db->pq(" SELECT prop, beamlinename, 1 as total, year, @@ -1049,16 +1166,17 @@ function _dewars_breakdown() { $group ORDER BY year DESC, country", $args); - foreach ($dewars as &$d) { + foreach ($dewars as &$d) { - $d['CODE'] = $this->dhl->get_code($d['COUNTRY']); - if ($d['COUNTRY'] && !$d['CODE']) print_r(array($d['COUNTRY'])); - - foreach(array('SHIPMENTS', 'DEWARS', 'HISTORY', 'DCS', 'CONTAINERS', 'CTA') as $k) $d[$k] = intval($d[$k]); - } + $d['CODE'] = $this->dhl->get_code($d['COUNTRY']); + if ($d['COUNTRY'] && !$d['CODE']) + print_r(array($d['COUNTRY'])); + foreach (array('SHIPMENTS', 'DEWARS', 'HISTORY', 'DCS', 'CONTAINERS', 'CTA') as $k) + $d[$k] = intval($d[$k]); + } - $this->_output($dewars); - } + $this->_output($dewars); + } } diff --git a/api/src/Shipment/Courier/DHL.php b/api/src/Shipment/Courier/DHL.php index f0797eeed..cfb93e16d 100644 --- a/api/src/Shipment/Courier/DHL.php +++ b/api/src/Shipment/Courier/DHL.php @@ -2,17 +2,17 @@ namespace SynchWeb\Shipment\Courier; -use DHL\Client\Web as WebserviceClient; -use DHL\Datatype\AM\PieceType; -use DHL\Datatype\GB\Piece; -use DHL\Entity\AM\GetQuote; -use DHL\Entity\GB\BookPURequest; -use DHL\Entity\GB\BookPUResponse; -use DHL\Entity\GB\CancelPURequest; -use DHL\Entity\GB\CancelPUResponse; -use DHL\Entity\GB\KnownTrackingRequest as Tracking; -use DHL\Entity\GB\ShipmentRequest; -use DHL\Entity\GB\ShipmentResponse; +use Mtc\Dhl\Client\Web as DHLWebClient; +use Mtc\Dhl\Datatype\AM\PieceType; +use Mtc\Dhl\Datatype\EU\Piece; +use Mtc\Dhl\Entity\AM\GetQuote; +use Mtc\Dhl\Entity\EU\BookPURequest; +use Mtc\Dhl\Entity\EU\BookPUResponse; +use Mtc\Dhl\Entity\EU\CancelPURequest; +use Mtc\Dhl\Entity\EU\CancelPUResponse; +use Mtc\Dhl\Entity\EU\ShipmentRequest; +use Mtc\Dhl\Entity\EU\ShipmentResponse; +use Mtc\Dhl\Entity\EU\KnownTrackingRequest; class DHL { @@ -105,24 +105,29 @@ function __construct($user = null, $password = null, $env = 'staging') function get_tracking_info($options) { - if (!array_key_exists('AWB', $options)) return; + $given_awb = array_key_exists('AWB', $options); + $given_lpnumber = array_key_exists('LPNumber', $options); + if (!$given_awb && !$given_lpnumber) return; - $request = new Tracking(); + $request = new KnownTrackingRequest(); $request->SiteID = $this->_user; $request->Password = $this->_password; $request->MessageReference = '12345678901234567890' . (string)time(); $request->MessageTime = date('c'); $request->LanguageCode = 'en'; - $request->AWBNumber = $options['AWB']; - $request->LevelOfDetails = array_key_exists('LAST_ONLY', $options) ? 'LAST_CHECK_POINT_ONLY' : 'ALL_CHECK_POINTS'; + if ($given_awb) $request->AWBNumber = $options['AWB']; + if ($given_lpnumber) $request->LPNumber = $options['LPNumber']; $request->PiecesEnabled = 'S'; + $request->LevelOfDetails = array_key_exists('LAST_ONLY', $options) ? 'LAST_CHECK_POINT_ONLY' : 'ALL_CHECK_POINTS'; - if ($this->log) file_put_contents('logs/trackingrequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['AWB'] . '.xml'), $request->toXML()); + $tracking_number = ($given_lpnumber) ? $options['LPNumber'] : $options['AWB']; - $client = new WebserviceClient($this->env); + if ($this->log) file_put_contents('./logs/trackingrequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $tracking_number . '.xml'), $request->toXML()); + + $client = new DHLWebClient($this->env); $xml = $client->call($request); - if ($this->log) file_put_contents('logs/trackingresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['AWB'] . '.xml'), $xml); + if ($this->log) file_put_contents('./logs/trackingresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $tracking_number . '.xml'), $xml); $xml = simplexml_load_string(str_replace('req:', '', $xml)); return $xml; @@ -130,6 +135,8 @@ function get_tracking_info($options) function create_awb($options) { + global $synchweb_version; + $shipment = new ShipmentRequest(); $shipment->SiteID = $this->_user; $shipment->Password = $this->_password; @@ -139,21 +146,21 @@ function create_awb($options) $shipment->RegionCode = $this->region; $shipment->RequestedPickupTime = 'Y'; $shipment->LanguageCode = 'en'; - $shipment->PiecesEnabled = 'Y'; + $shipment->SoftwareName = 'Synchweb'; + $shipment->SoftwareVersion = (isset($synchweb_version)) ? $synchweb_version : '4.0'; $shipment->Billing->ShippingPaymentType = $options['payee']; $shipment->Billing->ShipperAccountNumber = $options['accountnumber']; if ($options['payee'] != 'S') { $shipment->Billing->BillingAccountNumber = $options['accountnumber']; } - $shipment->Billing->DutyPaymentType = 'R'; - - $shipment->Dutiable->DeclaredValue = $options['declaredvalue']; - $shipment->Dutiable->DeclaredCurrency = 'GBP'; $shipment->Consignee->CompanyName = $options['receiver']['company']; - foreach (split("\n", $options['receiver']['address']) as $l) { - if ($l) $shipment->Consignee->addAddressLine($l); - } + + $reciever_address_lines = explode(PHP_EOL, rtrim($options['receiver']['address'])); + if (isset($reciever_address_lines[0])) $shipment->Consignee->addAddressLine1($reciever_address_lines[0]); + if (isset($reciever_address_lines[1])) $shipment->Consignee->addAddressLine2($reciever_address_lines[1]); + if (isset($reciever_address_lines[2])) $shipment->Consignee->addAddressLine3($reciever_address_lines[2]); + $shipment->Consignee->City = $options['receiver']['city']; $shipment->Consignee->PostalCode = $options['receiver']['postcode']; $shipment->Consignee->CountryName = $options['receiver']['country']; @@ -162,22 +169,19 @@ function create_awb($options) $shipment->Consignee->Contact->PhoneNumber = $options['receiver']['phone']; $shipment->Consignee->Contact->Email = $options['receiver']['email']; - $shipment->ShipmentDetails->NumberOfPieces = sizeof($options['pieces']); + $shipment->Reference->ReferenceID = $options['shipperid']; + $shipment->ShipmentDetails->WeightUnit = 'K'; $shipment->ShipmentDetails->GlobalProductCode = $options['service']; $shipment->ShipmentDetails->Date = array_key_exists('date', $options) ? $options['date'] : date('Y-m-d'); $shipment->ShipmentDetails->Contents = $options['description']; $shipment->ShipmentDetails->DimensionUnit = 'C'; $shipment->ShipmentDetails->CurrencyCode = 'GBP'; - $shipment->ShipmentDetails->DoorTo = 'DD'; - // $shipment->ShipmentDetails->IsDutiable = 'Y'; if (array_key_exists('notification', $options)) $shipment->Notification->EmailAddress = $options['notification']; if (array_key_exists('message', $options)) $shipment->Notification->Message = $options['message']; - $weight = 0; foreach ($options['pieces'] as $i => $d) { - $weight += $d['weight']; $piece = new Piece(); $piece->PieceID = ($i + 1); @@ -188,13 +192,14 @@ function create_awb($options) $shipment->ShipmentDetails->addPiece($piece); } - $shipment->ShipmentDetails->Weight = $weight; - - $shipment->Shipper->ShipperID = (string)rand(10000000, 9999999); + $shipment->Shipper->ShipperID = $options['shipperid']; $shipment->Shipper->CompanyName = $options['sender']['company']; - foreach (split("\n", $options['sender']['address']) as $l) { - if ($l) $shipment->Shipper->addAddressLine($l); - } + + $shipper_address_lines = explode(PHP_EOL, rtrim($options['sender']['address'])); + if (isset($shipper_address_lines[0])) $shipment->Shipper->addAddressLine1($shipper_address_lines[0]); + if (isset($shipper_address_lines[1])) $shipment->Shipper->addAddressLine2($shipper_address_lines[1]); + if (isset($shipper_address_lines[2])) $shipment->Shipper->addAddressLine3($shipper_address_lines[2]); + $shipment->Shipper->City = $options['sender']['city']; $shipment->Shipper->PostalCode = $options['sender']['postcode']; $shipment->Shipper->CountryCode = $this->_country_codes[$options['sender']['country']]; @@ -207,14 +212,13 @@ function create_awb($options) if ($this->log) file_put_contents('logs/shipmentrequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['sender']['name'] . '.xml'), $shipment->toXML()); - $client = new WebserviceClient($this->env); + $client = new DHLWebClient($this->env); $xml = $client->call($shipment); if ($this->log) file_put_contents('logs/shipmentresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['sender']['name'] . '.xml'), $xml); $response = new ShipmentResponse(); $response->initFromXML($xml); - // echo $response->toXML(); $pieces = array(); foreach ($response->Pieces as $p) { @@ -251,7 +255,7 @@ function request_pickup($options) $pickup->Place->LocationType = 'B'; $pickup->Place->CompanyName = $options['requestor']['company']; - $lines = split("\n", $options['requestor']['address']); + $lines = explode("\n", $options['requestor']['address']); $pickup->Place->Address1 = $lines[0]; if (sizeof($lines) > 1) { if ($lines[1]) $pickup->Place->Address2 = $lines[1]; @@ -276,14 +280,13 @@ function request_pickup($options) if ($this->log) file_put_contents('logs/pickuprequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['requestor']['name'] . '.xml'), $pickup->toXML()); - $client = new WebserviceClient($this->env); + $client = new DHLWebClient($this->env); $xml = $client->call($pickup); if ($this->log) file_put_contents('logs/pickupresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['requestor']['name'] . '.xml'), $xml); $response = new BookPUResponse(); $response->initFromXML($xml); - // echo $response->toXML(); return array( 'confirmationnumber' => $response->ConfirmationNumber, @@ -312,14 +315,13 @@ function cancel_pickup($options) if ($this->log) file_put_contents('logs/cancelpickuprequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['confirmationnumber'] . '.xml'), $cancelpickup->toXML()); - $client = new WebserviceClient($this->env); + $client = new DHLWebClient($this->env); $xml = $client->call($cancelpickup); if ($this->log) file_put_contents('logs/cancelpickupresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['confirmationnumber'] . '.xml'), $xml); $response = new CancelPUResponse(); $response->initFromXML($xml); - // echo $response->toXML(); return array(); } @@ -350,6 +352,7 @@ function get_quote($options) $quote->BkgDetails->DimensionUnit = 'CM'; $quote->BkgDetails->WeightUnit = 'KG'; $quote->BkgDetails->PaymentCountryCode = $this->_country_codes[$options['receiver']['country']]; + $quote->BkgDetails->PaymentAccountNumber = $options['payment_account_number']; $quote->From->CountryCode = $this->_country_codes[$options['sender']['country']]; $quote->From->Postalcode = $options['sender']['postcode']; @@ -364,7 +367,7 @@ function get_quote($options) if ($this->log) file_put_contents('logs/quoterequest_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['sender']['city'] . '.xml'), $quote->toXML()); - $client = new WebserviceClient($this->env); + $client = new DHLWebClient($this->env); $xml = $client->call($quote); if ($this->log) file_put_contents('logs/quoteresponse_' . date('Ymd-Hi') . '_' . str_replace(' ', '_', $options['sender']['city'] . '.xml'), $xml); @@ -382,6 +385,8 @@ function get_quote($options) $del = explode('-', (string)$q->DeliveryDate); $code = (string)$q->GlobalProductCode; + + // Skip over medical express if ($code == 'C') continue; array_push($products, array( diff --git a/api/src/Shipment/ShippingService.php b/api/src/Shipment/ShippingService.php new file mode 100644 index 000000000..647fba717 --- /dev/null +++ b/api/src/Shipment/ShippingService.php @@ -0,0 +1,154 @@ +shipping_api_url = $shipping_service_api_url; + $this->shipping_app_url = $shipping_service_app_url; + } + + + function _send_request($url, $type, $data, $expected_status_code) + { + $ch = curl_init($url); + $base_headers = $this->_build_headers(); + curl_setopt_array( + $ch, + array( + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_HTTPHEADER => $base_headers, + CURLOPT_TIMEOUT => 5 + ) + ); + switch ($type) { + case "POST": + curl_setopt_array( + $ch, + array( + CURLOPT_POST => TRUE, + CURLOPT_POSTFIELDS => json_encode($data) + ) + ); + break; + case "GET": + break; + case "PUT": + curl_setopt_array( + $ch, + array( + CURLOPT_CUSTOMREQUEST => "PUT", + CURLOPT_HTTPHEADER => array_merge($base_headers, array('Content-Length: ' . strlen(json_encode($data)))), + CURLOPT_POSTFIELDS => json_encode($data), + ) + ); + break; + } + $response = json_decode(curl_exec($ch), TRUE); + $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($status_code != $expected_status_code) { + throw new \Exception(json_encode(array('status' => $status_code, 'content' => $response))); + } + return $response; + } + + + function create_shipment($shipment_data) + { + return $this->_send_request( + $this->shipping_api_url . '/shipments/', + "POST", + $shipment_data, + 201 + ); + } + + + function create_shipment_by_journey_type($shipment_data, $journey_type) + { + return $this->_send_request( + $this->shipping_api_url . '/shipments/' . $journey_type, + "POST", + $shipment_data, + 201 + ); + } + + + function get_shipment($external_id, $journey_type) + { + return $this->_send_request( + $this->shipping_api_url . '/shipments/external_id/' . $external_id . '?journey_type=' . $journey_type, + "GET", + null, + 200 + ); + } + + + function update_shipment($external_id, $shipment_data, $journey_type) + { + return $this->_send_request( + $this->shipping_api_url . '/shipments/external_id/' . $external_id . '?journey_type=' . $journey_type, + "PUT", + $shipment_data, + 204 + ); + } + + + function dispatch_shipment($shipment_id, $pickup_requested) + { + $pickup_requested_str = ($pickup_requested) ? "true" : "false"; + return $this->_send_request( + $this->shipping_api_url . '/shipments/' . $shipment_id . '/dispatch?pickup_requested=' . $pickup_requested_str, + "POST", + null, + 201 + ); + } + + function get_awb_pdf_url($shipment_id) + { + return $this->shipping_app_url . '/shipments/' . $shipment_id . '/awb'; + } +} diff --git a/api/src/TemplateParser.php b/api/src/TemplateParser.php index ff3a32f39..9d97e2823 100644 --- a/api/src/TemplateParser.php +++ b/api/src/TemplateParser.php @@ -20,6 +20,7 @@ class TemplateParser var $params = array(); function __construct($db) { + $this->db = $db; } @@ -64,7 +65,7 @@ function visit_dir($options) { } if (array_key_exists('VISIT', $options)) { - $where = "WHERE CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) LIKE :1"; + $where = "WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) LIKE :1"; array_push($args, $options['VISIT']); } @@ -73,7 +74,7 @@ function visit_dir($options) { array_push($args, $options['DCID']); } - $visit = $this->db->pq("SELECT CONCAT(CONCAT(CONCAT(p.proposalcode, p.proposalnumber), '-'), s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename, pe.login as pilogin, dc.imagedirectory + $visit = $this->db->pq("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'YYYY') as year, s.beamlinename, pe.login as pilogin, dc.imagedirectory FROM blsession s INNER JOIN proposal p ON p.proposalid = s.proposalid INNER JOIN person pe ON pe.personid = p.personid diff --git a/api/src/User.php b/api/src/User.php deleted file mode 100644 index d05549530..000000000 --- a/api/src/User.php +++ /dev/null @@ -1,95 +0,0 @@ -db = $db; - $this->login = $login; - $this->app = $app; - - $this->personid = 0; - $this->perms = array(); - $this->groups = array(); - - $result = $this->db->pq("SELECT cache, personid, givenname, familyname FROM person p WHERE login=:1", array($login)); - - if (sizeof($result)) { - foreach(array('personid', 'givenname', 'familyname') as $f) { - $this->$f = $result[0][strtoupper($f)]; - } - - $this->_cache = $result[0]['CACHE'] ? json_decode($this->db->read($result[0]['CACHE']), True) : array(); - if (!$this->_cache) $this->_cache = array(); - $this->personid = intval($this->personid); - - $perms = $this->db->pq("SELECT p.type, g.name as usergroup - FROM permission p - INNER JOIN usergroup_has_permission uhp ON uhp.permissionid = p.permissionid - INNER JOIN usergroup g ON g.usergroupid = uhp.usergroupid - INNER JOIN usergroup_has_person uhpe ON uhpe.usergroupid = g.usergroupid - WHERE uhpe.personid=:1", array($this->personid)); - - foreach ($perms as $p) { - array_push($this->perms, $p['TYPE']); - if (!in_array($p['USERGROUP'], $this->groups)) array_push($this->groups, $p['USERGROUP']); - } - } - - } - - - // Check what user can do - function has($permission) { - return in_array($permission, $this->perms); - } - - - function can($permission) { - if(in_array($permission, $this->perms)) return true; - else $this->app->halt(403, json_encode(array('status' => 403, 'message' => 'Access Denied', 'title' => 'You do not have the permission: '.$permission))); - } - - function has_group($group) { - return in_array($group, $this->groups); - } - - - - // User cache - for saving partially filled forms, etc - function cache($key) { - return array_key_exists($key, $this->_cache) ? $this->_cache[$key] : null; - } - - - function set_cache($key, $data) { - $allowed_caches = array('shipment', 'container'); - - if (in_array($key, $allowed_caches)) { - $this->_cache[$key] = $data; - $this->db->pq("UPDATE person SET cache=:1 WHERE personid=:2", array(json_encode($this->_cache), $this->personid)); - - return true; - } - - return false; - } - - - - # TODO - # Should replace $this->user with $this->user->login - function __toString() { - return $this->login; - } - - # For JSONing a user - function __toArray() { - return array('login' => $this->login, 'personid' => $this->personid, 'permissions' => $this->perms); - } -} \ No newline at end of file diff --git a/api/src/Utils.php b/api/src/Utils.php new file mode 100644 index 000000000..0a7f2a764 --- /dev/null +++ b/api/src/Utils.php @@ -0,0 +1,39 @@ + $title, 'msg' => $msg)); + error_log('Database Error: ' . $msg); + + if (Utils::$exitOnError) { + exit(); + } + } + + public static function shouldLogUserActivityToDB($loginId): bool + { + global $log_activity_to_ispyb; + $log_activity = isset($log_activity_to_ispyb) ? $log_activity_to_ispyb : true; + + return $log_activity && $loginId; + } + + public static function getValueOrDefault($value, $default = false) + { + return (isset($value)) ? $value : $default; + } + + public static function filterParamFromUrl($url, $param): string + { + // Removes search parameter from URL and returns encoded URL + $redirect_url = preg_replace('/(&|\?)'.preg_quote($param).'=[^&]*$/', '', $url); + return urlencode(preg_replace('/(&|\?)'.preg_quote($param).'=[^&]*&/', '$1', $redirect_url)); + } +} diff --git a/api/tests/Controllers/AssignControllerTest.php b/api/tests/Controllers/AssignControllerTest.php new file mode 100644 index 000000000..8c0426f39 --- /dev/null +++ b/api/tests/Controllers/AssignControllerTest.php @@ -0,0 +1,290 @@ +slimStub = Mockery::mock('Slim\Slim'); + $this->dataLayerStub = $this->getMockBuilder(AssignData::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + $this->dbStub = $this->getMockBuilder(MySQL::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + + $this->user = new User("blah", 231312, "frod", "blegs", array("read", "write"), array(), array()); + $this->assignController = Mockery::mock('SynchWeb\Controllers\AssignController')->makePartial(); + $this->assignController->shouldReceive('_setup_routes')->times(1)->andReturn(Mockery::self()); + $this->assignController->__construct($this->slimStub, $this->dbStub, $this->user, $this->dataLayerStub); + + global $ip2bl; + $ip2bl = array(103 => 'i03'); + + global $bl_pv_map; + $bl_pv_map = array( + 'i02' => 'BL02I', + 'i03' => 'BL03I', + ); + global $bl_pv_env; + $bl_pv_env = 'EPICS_CA_ADDR_LIST_TEST=666.45.678.9'; + } + + protected function tearDown(): void + { + Mockery::close(); + Output::reset(); + } + + function setUpCommonResponse($responseTimes = 1): \Slim\Http\Response + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('response')->times($responseTimes)->andReturn($response); + $this->slimStub->shouldReceive('contentType')->times($responseTimes); + return $response; + } + + public function testAssignContainerReturnsZeroWhenNoContainerFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['cid'] = 4; + $this->assignController->args['pos'] = 5; + $this->dataLayerStub->expects($this->exactly(1))->method('getContainer')->with(3, 4)->willReturn(array()); + $this->dataLayerStub->expects($this->never())->method('assignContainer'); + + $this->assignController->assignContainer(); + $this->assertEquals(0, $response->getBody()); + } + public function testAssignContainerReturnsOneWhenContainerFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['cid'] = 4; + $this->assignController->args['pos'] = 5; + $this->dataLayerStub->expects($this->exactly(1))->method('getContainer')->with(3, 4)->willReturn(array(1,2,3)); + $this->dataLayerStub->expects($this->exactly(1))->method('assignContainer')->with(1, 5); + + $this->assignController->assignContainer(); + $this->assertEquals(1, $response->getBody()); + } + + public function testUnassignContainerReturnsZeroWhenNoContainerFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['cid'] = 4; + $this->assignController->args['pos'] = 5; + $this->dataLayerStub->expects($this->exactly(1))->method('getContainer')->with(3, 4)->willReturn(array()); + $this->dataLayerStub->expects($this->never())->method('unassignContainer'); + + $this->assignController->unassignContainer(); + $this->assertEquals(0, $response->getBody()); + } + public function testUnassignContainerReturnsOneWhenContainerFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['cid'] = 4; + $this->assignController->args['pos'] = 5; + $this->dataLayerStub->expects($this->exactly(1))->method('getContainer')->with(3, 4)->willReturn(array(1,2,3)); + $this->dataLayerStub->expects($this->exactly(1))->method('unassignContainer')->with(1); + + $this->assignController->unassignContainer(); + $this->assertEquals(1, $response->getBody()); + } + + public function testDeactivateDewarReturnsZeroWhenNoDewarFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['did'] = 4; + $this->assignController->proposalid = 66; + $this->dataLayerStub->expects($this->exactly(1))->method('getDewar')->with(4, 66, 3)->willReturn(array()); + $this->dataLayerStub->expects($this->never())->method('deactivateDewar'); + + $this->assignController->deactivateDewar(); + $this->assertEquals(0, $response->getBody()); + } + + public function testDeactivateDewarReturnsOneWhenDewarFound(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['visit'] = 3; + $this->assignController->args['did'] = 4; + $this->assignController->proposalid = 66; + $this->dataLayerStub->expects($this->exactly(1))->method('getDewar')->with(4, 66, 3)->willReturn(array(1,2,3)); + $this->dataLayerStub->expects($this->exactly(1))->method('deactivateDewar')->with(4); + + $this->assignController->deactivateDewar(); + $this->assertEquals(1, $response->getBody()); + } + + public function testGetBeamlineVisitsWithInvalidNoConfigReturnsNothing(): void + { + $response = $this->setUpCommonResponse(); + $_SERVER['REMOTE_ADDR'] = ''; + + $this->assignController->getBeamlineVisits(); + $this->assertEquals('[]', $response->getBody()); + } + + public function testGetBeamlineVisitsWithValidConfigReturnsNothing(): void + { + $response = $this->setUpCommonResponse(); + $_SERVER['REMOTE_ADDR'] = 'www.diamond.103.com'; + $this->dbStub->expects($this->exactly(1))->method('pq')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) < 1 AND TIMESTAMPDIFF('DAY', CURRENT_TIMESTAMP, s.enddate) < 2 AND s.beamlinename LIKE :1 ORDER BY s.startdate", array('i03'))->willReturn(array()); + $this->dbStub->expects($this->exactly(1))->method('paginate')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE p.proposalcode LIKE 'cm' AND s.beamlinename LIKE :1 AND s.enddate <= CURRENT_TIMESTAMP ORDER BY s.startdate DESC", array('i03', 0, 1))->willReturn(array()); + + $this->assignController->getBeamlineVisits(); + $this->assertEquals('[]', $response->getBody()); + } + + public function testGetBeamlineVisitsWithValidConfigAndVisitReturnsData(): void + { + $visitId = 123; + $response = $this->setUpCommonResponse(); + $_SERVER['REMOTE_ADDR'] = 'www.diamond.103.com'; + $result = ['VISIT' => $visitId, 'BL' => 'test03']; + $this->dbStub->expects($this->exactly(1))->method('pq')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) < 1 AND TIMESTAMPDIFF('DAY', CURRENT_TIMESTAMP, s.enddate) < 2 AND s.beamlinename LIKE :1 ORDER BY s.startdate", array('i03'))->willReturn(array($result)); + $this->dbStub->expects($this->exactly(1))->method('paginate')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE p.proposalcode LIKE 'cm' AND s.beamlinename LIKE :1 AND s.enddate <= CURRENT_TIMESTAMP ORDER BY s.startdate DESC", array('i03', 0, 1))->willReturn(array()); + + $this->assignController->getBeamlineVisits($visitId); + $this->assertEquals('{"VISIT":123,"BL":"test03"}', $response->getBody()); + } + + public function testGetBeamlineVisitsWithValidConfigAndInvalidVisitReturnsError(): void + { + $visitId = 123; + $_SERVER['REMOTE_ADDR'] = 'www.diamond.103.com'; + $result = ['VISIT' => 1230, 'BL' => 'test03']; + $this->dbStub->expects($this->exactly(1))->method('pq')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE TIMESTAMPDIFF('DAY', s.startdate, CURRENT_TIMESTAMP) < 1 AND TIMESTAMPDIFF('DAY', CURRENT_TIMESTAMP, s.enddate) < 2 AND s.beamlinename LIKE :1 ORDER BY s.startdate", array('i03'))->willReturn(array($result)); + $this->dbStub->expects($this->exactly(1))->method('paginate')->with("SELECT CONCAT(p.proposalcode, p.proposalnumber, '-', s.visit_number) as visit, TO_CHAR(s.startdate, 'DD-MM-YYYY HH24:MI') as st, TO_CHAR(s.enddate, 'DD-MM-YYYY HH24:MI') as en,s.beamlinename as bl FROM blsession s INNER JOIN proposal p ON (p.proposalid = s.proposalid) WHERE p.proposalcode LIKE 'cm' AND s.beamlinename LIKE :1 AND s.enddate <= CURRENT_TIMESTAMP ORDER BY s.startdate DESC", array('i03', 0, 1))->willReturn(array()); + + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such visit"}')->andThrow(new \Exception); + $this->expectException(\Exception::class); + + $this->assignController->getBeamlineVisits($visitId); + } + + public function testGetPuckNamesWithoutPropThrowsError(): void + { + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No proposal specified"}')->andThrow(new \Exception); + $this->expectException(\Exception::class); + + $this->assignController->getPuckNames(); + } + + public function testGetPuckNamesWithPropNoBlThrowsError(): void + { + $this->assignController->args['prop'] = 3; + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No beamline specified"}')->andThrow(new \Exception); + $this->expectException(\Exception::class); + + $this->assignController->getPuckNames(); + } + + public function testGetPuckNamesWithPropAndInvalidBlThrowsError(): void + { + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'test'; + + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such beamline"}')->andThrow(new \Exception); + $this->expectException(\Exception::class); + + $this->assignController->getPuckNames(); + } + + public function testGetPuckNamesWithPropAndValidBlReturnsNothing(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'i03'; + $this->assignController->proposalid = 3; + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array()); + $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); + + $this->assignController->getPuckNames(); + $this->assertEquals('[]', $response->getBody()); + } + + public function testGetPuckNamesWithPropAndValidBlSingleValReturnsData(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'i03'; + $this->assignController->proposalid = 3; + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => 'puck1', 'PUCK_12_NAME' => 'puck12')); + $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); + + $this->assignController->getPuckNames(); + $this->assertEquals('[{"id":1,"name":""},{"id":12,"name":""}]', $response->getBody()); + } + + public function testGetPuckNamesWithPropAndValidBlArrayWithNoDataValReturnsObfuscatedData(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'i03'; + $this->assignController->proposalid = 3; + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(12))); + $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); + + $this->assignController->getPuckNames(); + $this->assertEquals('[{"id":1,"name":"[Loaded]"},{"id":12,"name":"[Loaded]"}]', $response->getBody()); + } + + public function testGetPuckNamesWithPropAndValidBlArrayWithMatchingDataValReturnsRealData(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'i03'; + $this->assignController->proposalid = 3; + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(1230))); + $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); + + $this->assignController->getPuckNames(); + $this->assertEquals('[{"id":1,"name":"[Loaded]"},{"id":12,"name":1230}]', $response->getBody()); + } + + public function testGetPuckNamesWithPropAndValidBlArrayForStaffMemberReturnsRealData(): void + { + $response = $this->setUpCommonResponse(); + $this->assignController->args['prop'] = 3; + $this->assignController->args['bl'] = 'i03'; + $this->assignController->proposalid = 3; + $this->assignController->staff = true; + $this->assignController->shouldReceive('pv')->times(1)->andReturn(array('PUCK_1_NAME' => array(11), 'PUCK_12_NAME' => array(1231))); + $this->dataLayerStub->expects($this->exactly(1))->method('getContainerBarcodesForProposal')->with(3)->willReturn(array(['BARCODE' => 1230, 'BL' => 'test03'])); + + $this->assignController->getPuckNames(); + $this->assertEquals('[{"id":1,"name":11},{"id":12,"name":1231}]', $response->getBody()); + } +} diff --git a/api/tests/Controllers/AuthenticationControllerTest.php b/api/tests/Controllers/AuthenticationControllerTest.php new file mode 100644 index 000000000..11aa941ec --- /dev/null +++ b/api/tests/Controllers/AuthenticationControllerTest.php @@ -0,0 +1,217 @@ +fail("Exception not thrown"); + } + catch (\Exception $e) { + if ($e->getMessage() !== $errorMessage) { + throw $e; + } + } + } + + /** + * We expect slim to recieve method calls about request made and responses to those requests + */ + private function setupSlimStubsRequestAndResponse($expectedRequestCalls, $expectedReponseCalls) + { + $environment = \Slim\Environment::mock([ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => '/echo', + 'QUERY_STRING' => 'foo=bar' + ]); + $request = new \Slim\Http\Request($environment); + $this->slimStub->shouldReceive('request')->times($expectedRequestCalls)->andReturn($request); + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('response')->times($expectedReponseCalls)->andReturn($response); + } + + /** + * Set up auth controller with mocked authentication type in + */ + private function setUpAuthControllerWithMockedAuthType($authTypeMethod, $authTypeReturnValue) { + global $authentication_type; + $authentication_type="test"; + $authTypeMock = Mockery::mock('AuthenticationType'); + $authTypeMock->shouldReceive($authTypeMethod)->andReturn($authTypeReturnValue); + $mockAuthFactory = Mockery::mock('AuthenticationTypeFactory'); + $mockAuthFactory->shouldReceive('create')->with($authentication_type)->andReturn($authTypeMock); + return new AuthenticationController($this->slimStub, $this->dataLayerStub, false, $mockAuthFactory); + } + + protected function setUp(): void + { + global $authentication_type; + $authentication_type = "cas"; + Output::reset(); + $this->slimStub = Mockery::mock('Slim\Slim'); + $this->slimStub->shouldReceive('post')->times(2)->andReturn(new AppStub()); + $this->slimStub->shouldReceive('get')->times(4); + $this->dataLayerStub = $this->getMockBuilder(AuthenticationData::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + } + + + protected function tearDown(): void + { + Mockery::close(); + Output::reset(); + } + + public function testConstructorSetsUpExpectedRoutes(): void + { + new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + } + + public function testGivenUnknownAuthTypeWhenCheckThenInternalServerError(): void + { + global $authentication_type; + $authentication_type = "unknown"; + $this->setupSlimStubsRequestAndResponse(0,1); + + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + + $this->expectExceptionOn( function () use ($authService){ + $authService->check(); + }); + + $this->assertContains('X-PHP-Response-Code: 500', Output::$headers); + } + + public function testCheckResultsInitiallyFails(): void + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('response')->times(1)->andReturn($response); + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + + $this->expectExceptionOn( function () use ($authService){ + $authService->check(); + }); + + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 400', Output::$headers); + } + + public function testGetUserWithNoTokenInitiallyReturnsUnauthorised(): void + { + $this->setupSlimStubsRequestAndResponse(2, 1); + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + + $this->expectExceptionOn( function () use ($authService){ + $authService->getUser(); + }); + + $this->assertContains('X-PHP-Response-Code: 401', Output::$headers); + } + + public function testValidateAuthenticationInitiallyFails(): void + { + $this->setupSlimStubsRequestAndResponse(2,1); + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + + $this->expectExceptionOn( function () use ($authService){ + $authService->validateAuthentication(); + }); + + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 401', Output::$headers); + } + + public function testCodeAuthenticationInitiallyFailsWhenAuthenticationTypeReturnsFalse(): void + { + $request = $this->setupSlimStubsRequestAndResponse(1, 1); + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); //Use default CAS auth to return type + + $this->expectExceptionOn( function () use ($authService){ + $authService->authenticateByCode(); + }); + + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 401', Output::$headers); + } + + public function testCodeAuthenticationWhenGetValidFedIdReturnsSuccess(): void + { + global $jwt_key; + $jwt_key = "test_key"; + + $expectedFedID = "FedId012"; + $_SERVER['HTTP_HOST'] = "host"; + + $this->setupSlimStubsRequestAndResponse(1, 1); + $authService = $this->setUpAuthControllerWithMockedAuthType("authenticateByCode", $expectedFedID); + + $this->expectExceptionOn( function () use ($authService){ + $authService->authenticateByCode(); + }); + + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 200', Output::$headers); + } + + + public function testNoSSOAuthorisationRedirect(): void + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('response')->times(1)->andReturn($response); + + $authService = new AuthenticationController($this->slimStub, $this->dataLayerStub, false); + + $this->expectExceptionOn( function () use ($authService){ + $authService->authorise(); + }); + + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 501', Output::$headers); + } + + public function testValidateAuthorisationRedirect(): void + { + global $cas_sso; + $cas_sso = true; + + $_SERVER['HTTP_REFERER'] = "localhost/test"; + $expectedURL = "expectedRedirectURL"; + + $this->setupSlimStubsRequestAndResponse(0, 1); + $authService = $this->setUpAuthControllerWithMockedAuthType("authorise", $expectedURL); + + $this->expectExceptionOn( function () use ($authService){ + $authService->authorise(); // once a function calls an exception the stack finishes to add finally to run final tests. + }); + $this->assertContains('Content-Type: application/json', Output::$headers); + $this->assertContains('X-PHP-Response-Code: 302', Output::$headers); + } +} \ No newline at end of file diff --git a/api/tests/Controllers/UserControllerTest.php b/api/tests/Controllers/UserControllerTest.php new file mode 100644 index 000000000..8c31e82b1 --- /dev/null +++ b/api/tests/Controllers/UserControllerTest.php @@ -0,0 +1,566 @@ +slimStub = Mockery::mock('Slim\Slim'); + $this->dataLayerStub = $this->getMockBuilder(UserData::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + $this->dbStub = $this->getMockBuilder(MySQL::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + + $this->user = new User("blah", 231312, "frod", "blegs", array("read", "write"), array(), array()); + $this->userController = Mockery::mock('SynchWeb\Controllers\UserController')->makePartial(); + $this->userController->shouldReceive('_setup_routes')->times(1)->andReturn(Mockery::self());; + $this->userController->__construct($this->slimStub, $this->dbStub, $this->user, $this->dataLayerStub); + } + + + protected function tearDown(): void + { + Mockery::close(); + Output::reset(); + } + + function setUpCommonResponse($responseTimes = 1): \Slim\Http\Response + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('response')->times($responseTimes)->andReturn($response); + $this->slimStub->shouldReceive('contentType')->times($responseTimes); + return $response; + } + + public function testGetCurrentUserReturnsCorrectData(): void + { + $response = $this->setUpCommonResponse(); + + $this->userController->getCurrentUser(); + $this->assertEquals('{"personid":231312,"user":"blah","givenname":"frod","permissions":["read","write"],"is_staff":false,"visits":[],"ty":null}', $response->getBody()); + } + + public function testGetTimeReturnsExpectedData(): void + { + $response = $this->setUpCommonResponse(); + + $this->userController->getTime(); + $this->assertStringStartsWith('{"TIME":"', $response->getBody()); + } + + public function testGetGroupsFailsIfLackingPermissions(): void + { + $response = $this->setUpCommonResponse(); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}'); + + $this->userController->_get_groups(); + } + + public function testGetGroupsCompletesIfHaveCorrectPermissions(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->slimStub->shouldReceive('halt')->times(0); + + $this->userController->_get_groups(); + $this->assertEquals('null', $response->getBody()); + } + + public function testGetGroupsWithGidSpecifiedReturnsNoData(): void + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('contentType')->times(0); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->dataLayerStub->expects($this->exactly(1))->method('getGroups')->with(3)->willReturn(array()); + + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such group"}'); + $this->userController->_get_groups(); + $this->assertEmpty($response->getBody()); + } + + public function testGetGroupsWithGidSpecifiedReturnsData(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->dataLayerStub->expects($this->exactly(1))->method('getGroups')->with(3)->willReturn(array(61,2,3)); + + $this->userController->_get_groups(); + $this->assertEquals(61, $response->getBody()); + } + + public function testAddGroupFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_add_group(); + } + + public function testAddGroupWithNameSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['NAME'] = 'test group name'; + $this->dataLayerStub->expects($this->exactly(1))->method('addGroup')->with('test group name')->willReturn(61); + + $this->userController->_add_group(); + $this->assertEquals('{"USERGROUPID":61}', $response->getBody()); + } + + public function testAddGroupWithNoNameSpecifiedReturnsError(): void + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('contentType')->times(0); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['NONAME'] = 'test group'; + + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No group name"}'); + $this->userController->_add_group(); + $this->assertEmpty($response->getBody()); + } + + public function testUpdateGroupFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_update_group(); + } + + public function testUpdateGroupWithGidAndNameSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->userController->args['NAME'] = 'new name'; + $this->dataLayerStub->expects($this->exactly(1))->method('updateGroup')->with(3, 'new name')->willReturn(61); + + $this->userController->_update_group(); + $this->assertEquals('{"NAME":"new name"}', $response->getBody()); + } + + public function testUpdateGroupWithNoGidSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: gid'); + array_push($this->user->perms, 'manage_groups'); + + $this->userController->_update_group(); + } + + public function testAddGroupPermissionFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_add_group_permission(); + } + + public function testAddGroupPermissionWithGidAndPidSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->userController->args['pid'] = 6; + $this->dataLayerStub->expects($this->exactly(1))->method('addGroupPermission')->with(3, 6)->willReturn(61); + + $this->userController->_add_group_permission(); + $this->assertEquals('{"USERGROUPID":3,"PERMISSIONID":6}', $response->getBody()); + } + + public function testAddGroupPermissionWithNoGidSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: gid'); + array_push($this->user->perms, 'manage_groups'); + + $this->userController->_add_group_permission(); + } + + public function testRemoveGroupPermissionFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_remove_group_permission(); + } + + public function testRemoveGroupPermissionWithGidAndPidSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->userController->args['pid'] = 6; + $this->dataLayerStub->expects($this->exactly(1))->method('removeGroupPermission')->with(3, 6)->willReturn(61); + + $this->userController->_remove_group_permission(); + $this->assertEquals(1, $response->getBody()); + } + + public function testRemoveGroupPermissionWithNoGidSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: gid'); + array_push($this->user->perms, 'manage_groups'); + + $this->userController->_remove_group_permission(); + } + + public function testGetUsersWithInvalidPersonIdReturnsNoData(): void + { + $this->userController->args['PERSONID'] = 88; + $this->dataLayerStub->expects($this->exactly(1))->method('getUsers')->with(false, false, '', '', '', '', '', 88, false, 231312, '', '', '', '', 15, true)->willReturn(array()); + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such user"}')->andThrow(new \Exception); + $this->expectException(\Exception::class); + + $this->userController->_get_users(); + } + + public function testGetUsersWithPersonIdReturnsNoTotalData(): void + { + $response = $this->setUpCommonResponse(); + $this->userController->args['PERSONID'] = 88; + $this->dataLayerStub->expects($this->exactly(1))->method('getUsers')->with(false, false, '', '', '', '', '', 88, false, 231312, '', '', '', '', 15, true)->willReturn(array(9)); + + $this->userController->_get_users(); + $this->assertEquals(9, $response->getBody()); + } + + public function testGetUsersWithoutPersonIdReturnsTotalsWithResults(): void + { + $response = $this->setUpCommonResponse(); + $this->userController->_get_users(); + $this->assertEquals('{"total":null,"data":null}', $response->getBody()); + } + + public function testCheckLoginFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_users"}')->andThrow(new \Exception); + + $this->userController->_check_login(); + } + + public function testCheckLoginWithValidLoginSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_users'); + $this->userController->args['LOGIN'] = 'fdsfdsfsdfs'; + $this->dataLayerStub->expects($this->exactly(1))->method('checkLogin')->with('fdsfdsfsdfs')->willReturn(array(61)); + + $this->userController->_check_login(); + $this->assertEquals('{}', $response->getBody()); + } + + public function testCheckLoginWithInvalidLoginSpecifiedReturnsCorrectly(): void + { + array_push($this->user->perms, 'manage_users'); + $this->userController->args['LOGIN'] = 'fdsfdsfsdfs'; + $this->dataLayerStub->expects($this->exactly(1))->method('checkLogin')->with('fdsfdsfsdfs')->willReturn(array()); + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"Login not used"}')->andThrow(new \Exception); + + $this->userController->_check_login(); + } + + public function testUpdateGroupWithNoLoginSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: LOGIN'); + array_push($this->user->perms, 'manage_users'); + + $this->userController->_check_login(); + } + + public function testAddUserFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_users"}')->andThrow(new \Exception); + + $this->userController->_add_user(); + } + + public function testAddUserWithValidInputsSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_users'); + $this->userController->args['LOGIN'] = 'loginId'; + $this->userController->args['GIVENNAME'] = 'given'; + $this->userController->args['FAMILYNAME'] = 'family'; + $this->dataLayerStub->expects($this->exactly(1))->method('addUser')->with( + $this->userController->args['LOGIN'], + $this->userController->args['GIVENNAME'], + $this->userController->args['FAMILYNAME'], + null)->willReturn(array(61)); + + $this->userController->_add_user(); + $this->assertEquals('{"PERSONID":[61]}', $response->getBody()); + } + + public function testAddUserWithValidInputsAndEmailSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_users'); + $this->userController->args['LOGIN'] = 'loginId'; + $this->userController->args['GIVENNAME'] = 'given'; + $this->userController->args['FAMILYNAME'] = 'family'; + $this->userController->args['EMAILADDRESS'] = 'email'; + $this->dataLayerStub->expects($this->exactly(1))->method('addUser')->with( + $this->userController->args['LOGIN'], + $this->userController->args['GIVENNAME'], + $this->userController->args['FAMILYNAME'], + $this->userController->args['EMAILADDRESS'])->willReturn(array(61)); + + $this->userController->_add_user(); + $this->assertEquals('{"PERSONID":[61]}', $response->getBody()); + } + + public function testUpdateUserWithInvalidPersonIdReturnsNoData(): void + { + $this->userController->args['PERSONID'] = 88; + $this->dataLayerStub->expects($this->exactly(1))->method('getUser')->with(231312, null, 88)->willReturn(array()); + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such person"}')->andThrow(new \Exception); + + $this->userController->_update_user(); + } + + public function testUpdateUserWithValidPersonIdAndLabIdReturnsData(): void + { + $this->setUpCommonResponse(2); + $this->userController->args['PERSONID'] = 88; + $result = ['PERSONID' => 88, 'FAMILYNAME' => 'family', 'LABORATORYID' => 666]; + $results = array($result); + $labResult = ['NAME' => 'dls', 'ADDRESS' => 'diamond', 'CITY' => 'didcot', 'POSTCODE' => 'OX15', 'COUNTRY' => 'uk']; + $labResults = array($labResult); + + $this->dataLayerStub->expects($this->exactly(2))->method('getUser')->with(231312, null, 88)->willReturn($results); + $this->dataLayerStub->expects($this->exactly(1))->method('updateUser'); + $this->dataLayerStub->expects($this->exactly(2))->method('getLaboratory')->with(666)->willReturn($labResults); + $this->dataLayerStub->expects($this->exactly(1))->method('updateLaboratory'); + + $this->userController->_update_user(); + } + + public function testUpdateUserWithValidPersonIdAndNoLabIdReturnsData(): void + { + $this->setUpCommonResponse(2); + $this->userController->args['PERSONID'] = 88; + $result = ['PERSONID' => 88, 'FAMILYNAME' => 'family', 'LABORATORYID' => null]; + $results = array($result); + $labResult = ['NAME' => 'dls', 'ADDRESS' => 'diamond', 'CITY' => 'didcot', 'POSTCODE' => 'OX15', 'COUNTRY' => 'uk']; + $labResults = array($labResult); + + $this->dataLayerStub->expects($this->exactly(2))->method('getUser')->with(231312, null, 88)->willReturn($results); + $this->dataLayerStub->expects($this->exactly(1))->method('updateUser'); + $this->dataLayerStub->expects($this->exactly(1))->method('getLaboratory')->willReturn($labResults); + $this->dataLayerStub->expects($this->exactly(1))->method('updateLaboratory'); + + $this->userController->_update_user(); + } + + public function testUpdateUserWithoutPersonIdReturnsTotalsWithResults(): void + { + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No person specified"}')->andThrow(new \Exception); + $this->userController->_update_user(); + } + + public function testAddGroupUserFailsIfLackingUsers(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_add_group_user(); + } + + public function testAddGroupUserWithGidAndPeidSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->userController->args['peid'] = 6; + $this->dataLayerStub->expects($this->exactly(1))->method('addGroupUser')->with(6, 3)->willReturn(61); + + $this->userController->_add_group_user(); + $this->assertEquals('{"USERGROUPID":3,"PERSONID":6}', $response->getBody()); + } + + public function testAddGroupUserWithNoPeidSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: peid'); + array_push($this->user->perms, 'manage_groups'); + + $this->userController->_add_group_user(); + } + + public function testRemoveGroupUserFailsIfLackingUsers(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_groups"}')->andThrow(new \Exception); + + $this->userController->_remove_group_user(); + } + + public function testRemoveGroupUserWithGidAndPeidSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_groups'); + $this->userController->args['gid'] = 3; + $this->userController->args['peid'] = 6; + $this->dataLayerStub->expects($this->exactly(1))->method('removeGroupUser')->with(6, 3)->willReturn(61); + + $this->userController->_remove_group_user(); + $this->assertEquals(1, $response->getBody()); + } + + public function testRemoveGroupUserWithNoPeidSpecifiedReturnsError(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Missing propery: peid'); + array_push($this->user->perms, 'manage_groups'); + + $this->userController->_remove_group_user(); + } + + public function testAddPermissionFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_perms"}')->andThrow(new \Exception); + + $this->userController->_add_permission(); + } + + public function testAddPermissionWithValidInputsSpecifiedReturnsCorrectly(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_perms'); + $this->userController->args['TYPE'] = 'permission type'; + $this->userController->args['DESCRIPTION'] = 'permission desc'; + $this->dataLayerStub->expects($this->exactly(1))->method('addPermission')->with( + $this->userController->args['TYPE'], + $this->userController->args['DESCRIPTION'])->willReturn(array(25)); + + $this->userController->_add_Permission(); + $this->assertEquals('{"PERMISSIONID":[25]}', $response->getBody()); + } + + public function testUpdatePermissionFailsIfLackingPermissions(): void + { + // note, we artificially throw an exception here, to ensure the test finishes at the point of halt() + $this->expectException(\Exception::class); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_perms"}')->andThrow(new \Exception); + + $this->userController->_add_permission(); + } + + public function testUpdatePermissionWithValidInputsReturnsData(): void + { + $response = $this->setUpCommonResponse(); + $this->userController->args['PERSONID'] = 88; + array_push($this->user->perms, 'manage_perms'); + $this->userController->args['pid'] = 44; + $this->userController->args['TYPE'] = 'permission type'; + $this->userController->args['DESCRIPTION'] = 'permission desc'; + $this->dataLayerStub->expects($this->exactly(1))->method('updatePermission')->with(44, 'permission type', 'permission desc')->willReturn(array()); + + $this->userController->_update_permission(); + $this->assertEquals('{"TYPE":"permission type","DESCRIPTION":"permission desc"}', $response->getBody()); + } + + public function testUpdatePermissionWithValidInputsButNoDescriptionReturnsData(): void + { + $response = $this->setUpCommonResponse(); + $this->userController->args['PERSONID'] = 88; + array_push($this->user->perms, 'manage_perms'); + $this->userController->args['pid'] = 44; + $this->userController->args['TYPE'] = 'permission type'; + $this->dataLayerStub->expects($this->exactly(1))->method('updatePermission')->with(44, 'permission type', '')->willReturn(array()); + + $this->userController->_update_permission(); + $this->assertEquals('{"TYPE":"permission type","DESCRIPTION":""}', $response->getBody()); + } + + public function testGetPermissionsFailsIfLackingPermissions(): void + { + $response = $this->setUpCommonResponse(); + $this->slimStub->shouldReceive('halt')->times(1)->with(403, '{"status":403,"message":"Access Denied","title":"You do not have the permission: manage_perms"}'); + + $this->userController->_get_Permissions(); + } + + public function testGetPermissionsWithNoPidSpecifiedReturnsTotalData(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_perms'); + $this->dataLayerStub->expects($this->exactly(2))->method('getPermissions'); + + $this->userController->_get_Permissions(); + $this->assertEquals('{"total":null,"data":null}', $response->getBody()); + } + + public function testGetPermissionsWithPidSpecifiedReturnsNoData(): void + { + $response = new \Slim\Http\Response(); + $this->slimStub->shouldReceive('contentType')->times(0); + array_push($this->user->perms, 'manage_perms'); + $this->userController->args['pid'] = 3; + $this->dataLayerStub->expects($this->exactly(1))->method('getPermissions')->with(false, '', '', 3, 15, 0)->willReturn(array()); + + $this->slimStub->shouldReceive('halt')->times(1)->with(400, '{"status":400,"message":"No such permission"}'); + $this->userController->_get_Permissions(); + $this->assertEmpty($response->getBody()); + } + + public function testGetPermissionsWithPidSpecifiedReturnsData(): void + { + $response = $this->setUpCommonResponse(); + array_push($this->user->perms, 'manage_perms'); + $this->userController->args['pid'] = 3; + $this->dataLayerStub->expects($this->exactly(1))->method('getPermissions')->with(false, '', '', 3, 15, 0)->willReturn(array(61,2,3)); + + $this->userController->_get_permissions(); + $this->assertEquals(61, $response->getBody()); + } +} \ No newline at end of file diff --git a/api/tests/Controllers/Utils/Functions.php b/api/tests/Controllers/Utils/Functions.php new file mode 100644 index 000000000..a33d9e24f --- /dev/null +++ b/api/tests/Controllers/Utils/Functions.php @@ -0,0 +1,39 @@ +getFunctionMock('SynchWeb', "error_log"); + $log->expects($this->once())->with("Database Error: Database connection for type 'invalid' does not exist."); + Utils::$exitOnError = false; + + $connFactory = new DatabaseConnectionFactory(); + $connFactory->get('invalid'); + } + + /** + * @runInSeparateProcess // Needed to allow db mocking + * @preserveGlobalState disabled + */ + public function testErrorIsLoggedForUnspecifiedDbType(): void + { + global $isb; + $isb = array('user' => 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb', 'port' => '80'); + $log = $this->getFunctionMock(__NAMESPACE__, "error_log"); + $log->expects($this->once())->with("Database type variable, dbtype, is not specified in config.php - defaulting to MySql."); + Utils::$exitOnError = false; + + $this->expectWarning(\mysqli_sql_exception::class); + + $connFactory = new DatabaseConnectionFactory(); + $connFactory->get(''); + } +} \ No newline at end of file diff --git a/api/tests/Database/DatabaseFactoryTest.php b/api/tests/Database/DatabaseFactoryTest.php new file mode 100644 index 000000000..8d2ae6720 --- /dev/null +++ b/api/tests/Database/DatabaseFactoryTest.php @@ -0,0 +1,90 @@ +createStub(DatabaseConnectionFactory::class); + $this->dbFactory = new DatabaseFactory($mockConnection); + } + + public function testCanBeInstantiated(): void + { + // fwrite(STDERR, print_r("Quick debugging can be done in the tests using this to generate output", TRUE)); + $this->assertInstanceOf( + DatabaseFactory::class , + new DatabaseFactory(new DatabaseConnectionFactory()) + ); + } + + public function testGetThrowsExceptionDueToInvalidConnectionDetails(): void + { + global $isb, $dbtype, $app; + $dbtype = "MySQL"; + $isb = array('user' => 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb', 'port' => '80'); + + $this->expectWarning(\mysqli_sql_exception::class); + + $dbFactory = new DatabaseFactory(new DatabaseConnectionFactory()); + $dbFactory->get(); + } + + /** + * @runInSeparateProcess // Needed to allow db mocking + * @preserveGlobalState disabled + */ + public function testErrorIsLoggedForUnrecognisedDatabaseType(): void + { + global $dbtype; + $dbtype = "obscure-db"; + + $log = $this->getFunctionMock(__NAMESPACE__, "error_log"); + $log->expects($this->once())->with("Database type '$dbtype' not configured."); + + $this->dbFactory->get(); + } + + /** + * @runInSeparateProcess // Needed to allow db mocking + * @preserveGlobalState disabled + */ + public function testErrorIsLoggedForDatabaseTypeClassThatDoesNotExist(): void + { + global $dbtype; + $dbtype = "obscure-db2"; + $this->dbFactory->database_types[$dbtype] = ["dbClassName" =>$dbtype, "dataConnectionName" => 'MySQL']; + + $log = $this->getFunctionMock(__NAMESPACE__, "error_log"); + $log->expects($this->once())->with("Database class 'SynchWeb\Database\Type\obscure-db2' does not exist."); + + $this->dbFactory->get(); + } + + /** + * @runInSeparateProcess // Needed to allow db mocking + * @preserveGlobalState disabled + */ + public function testMySQLObjectCreatedWithExpectedParameters(): void + { + global $isb, $dbtype; + $dbtype = "MySQL"; + $isb = array('user' => 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb', 'port' => '80'); + + $dblMock = Mockery::mock('overload:SynchWeb\Database\Type\MySQL'); + $dblMock->shouldReceive('__construct')->once(); + + $this->dbFactory->get(); + } +} \ No newline at end of file diff --git a/api/tests/Database/DatabaseQueryBuilderTest.php b/api/tests/Database/DatabaseQueryBuilderTest.php new file mode 100644 index 000000000..4a95ecc26 --- /dev/null +++ b/api/tests/Database/DatabaseQueryBuilderTest.php @@ -0,0 +1,171 @@ +getMockForAbstractClass(DatabaseParent::class, [""]); + + if ($expected_sql) + { + $db->expects($this->once()) + ->method('pq') + ->with( + $this->equalTo($expected_sql), + $this->equalTo($expected_values) + ); + } + else + { + $db->expects($this->never()) + ->method('pq'); + } + + return new DatabaseQueryBuilder($db); + } + + public function testForASingleSetWithIDDBUpdateIsCorrect() + { + $expected_table = "table"; + + $fields = [["fieldname", "value"]]; + $expected_id_name = "id"; + $expected_id_value = 3; + + $expected_sql = "UPDATE {$expected_table} SET {$fields[0][0]}=:1 WHERE {$expected_id_name}=:2"; + + $dbh = $this->create_database_helper($fields, $expected_sql, $expected_id_value); + foreach ($fields as $field) + { + $dbh->patch($field[0], $field[1]); + } + $dbh->whereIdEquals($expected_id_name, $expected_id_value) + ->update($expected_table); + } + + public function testForASingleDoubleSetWithIDDBUpdateIsCorrect() + { + $expected_table = "table2"; + + $fields = [ + ["fieldname", "value"], + ["fieldname2", 32] + ]; + $expected_id_name = "id"; + $expected_id_value = 3; + + $expected_sql = "UPDATE {$expected_table} SET {$fields[0][0]}=:1, {$fields[1][0]}=:2 WHERE {$expected_id_name}=:3"; + + $dbh = $this->create_database_helper($fields, $expected_sql, $expected_id_value); + foreach ($fields as $field) + { + $dbh->patch($field[0], $field[1]); + } + $dbh->whereIdEquals($expected_id_name, $expected_id_value) + ->update($expected_table); + } + + public function testForANullPatchValueValueIsNotSetInDBandFluentInterfaceForPatch() + { + $expected_table = "table2"; + + $fields = [ + ["fieldname", "value"], + ["null_field", null], + ["fieldname2", 32] + ]; + $expected_id_name = "id"; + $expected_id_value = 3; + + $expected_sql = "UPDATE {$expected_table} SET {$fields[0][0]}=:1, {$fields[2][0]}=:2 WHERE {$expected_id_name}=:3"; + + $dbh = $this->create_database_helper($fields, $expected_sql, $expected_id_value); + foreach ($fields as $field) + { + $dbh = $dbh->patch($field[0], $field[1]); + } + $dbh->whereIdEquals($expected_id_name, $expected_id_value) + ->update($expected_table); + } + + public function testWithNoFieldsOnUpdateNoDatabasepushIsDone() + { + $expected_table = "table2"; + + $fields = []; + $expected_id_name = "id"; + $expected_id_value = 3; + + $dbh = $this->create_database_helper($fields, null, $expected_id_value); + $dbh->whereIdEquals($expected_id_name, $expected_id_value) + ->update($expected_table); + } + + public function testWithNoFieldsOnInsertNoDatabasepushIsDone() + { + $expected_table = "table2"; + + $fields = []; + $expected_id_name = "id"; + $expected_id_value = 3; + + $dbh = $this->create_database_helper($fields, null, $expected_id_value); + $dbh->whereIdEquals($expected_id_name, $expected_id_value) + ->insert($expected_table); + } + + public function testForASingleInsertDBInsertStatementIsCorrect() + { + $expected_table = "table"; + + $fields = [["fieldname", "value"]]; + + $expected_sql = "INSERT INTO {$expected_table} ( {$fields[0][0]} ) VALUES ( :1 )"; + + $dbh = $this->create_database_helper($fields, $expected_sql); + foreach ($fields as $field) + { + $dbh->patch($field[0], $field[1]); + } + $dbh->insert($expected_table); + } + + public function testWithTwoValuesWhenInsertingDBInsertIsCorrect() + { + $expected_table = "table2"; + + $fields = [ + ["fieldname", "value"], + ["fieldname2", 32] + ]; + + $expected_sql = "INSERT INTO {$expected_table} ( {$fields[0][0]}, {$fields[1][0]} ) VALUES ( :1, :2 )"; + + $dbh = $this->create_database_helper($fields, $expected_sql); + foreach ($fields as $field) + { + $dbh->patch($field[0], $field[1]); + } + $dbh->insert($expected_table); + } +} diff --git a/api/tests/Database/Type/MySqlTest.php b/api/tests/Database/Type/MySqlTest.php new file mode 100644 index 000000000..f76a1321d --- /dev/null +++ b/api/tests/Database/Type/MySqlTest.php @@ -0,0 +1,142 @@ +connStub->expects($this->exactly(1))->method('prepare')->willReturn($this->stmtStub); + if ($bindArgs) { + $this->stmtStub->expects($this->exactly(1))->method('bind_param')->willReturn(true); + } + $this->stmtStub->expects($this->exactly(1))->method('execute')->willReturn(true); + + $log = $this->setupError("something bad happened...."); + $log->expects($this->never()); + } + + private function setupError($errorMessage) + { + $errorMsg = $this->getFunctionMock(__NAMESPACE__, "mysqli_error"); + $errorMsg->expects($this->any())->willReturn($errorMessage); + Utils::$exitOnError = false; + $log = $this->getFunctionMock('SynchWeb', "error_log"); + return $log; + } + + protected function setUp(): void + { + $this->connStub = $this->getMockBuilder(mysqli::class) + ->getMock(); + $this->db = new MySQL($this->connStub); + + $this->stmtStub = $this->getMockBuilder(\mysqli_stmt::class) + ->disableOriginalConstructor() + ->onlyMethods(['bind_param', 'execute', 'get_result', 'close']) + ->getMock(); + + } + + public function testGetLastQueryReturnsCorrectlyForZeroArgs(): void + { + $this->assertEmpty($this->db->getLastQuery()); + } + + public function testPqFailsWithExpectedError(): void + { + $expectedError = "something bad happened...."; + $log = $this->setupError($expectedError); + $log->expects($this->once())->with("Database Error: " . $expectedError); + + $this->db->pq("SELECT BLAH FROM BLAH", array()); + } + + public function testPqFailsWithExpectedErrorAtExecute(): void + { + $this->connStub->expects($this->exactly(1))->method('prepare')->willReturn($this->stmtStub); + + $expectedError = "something bad happened...."; + $log = $this->setupError($expectedError); + $log->expects($this->once())->with("Database Error: " . $expectedError); + + $this->db->pq("SELECT BLAH FROM BLAH", array()); + } + + public function testPqCompletesCleanlyWithStubs(): void + { + $this->connStub->expects($this->exactly(1))->method('prepare')->willReturn($this->stmtStub); + $this->stmtStub->expects($this->exactly(1))->method('execute')->willReturn(true); + + $log = $this->setupError("something bad happened...."); + $log->expects($this->never()); + + $result = $this->db->pq("SELECT BLAH FROM BLAH", array()); + $this->assertEmpty($result); + } + + public function testPqCompletesCleanlyWithStubsAndQueryArgs(): void + { + $this->stubOutMethodsToAllowCleanCompletion(); + $args = array('one', 'two', 'three'); + + $result = $this->db->pq("SELECT BLAH FROM BLAH", $args); + + $this->assertEmpty($result); + } + + public function testLastQueryReturnsValidStatmentIgnoringArgs(): void + { + $this->stubOutMethodsToAllowCleanCompletion(); + $args = array('one', 'two', 'three'); + $query = "SELECT BLAH FROM BLAH"; + + $this->db->pq($query, $args); + + $this->assertEquals($query, $this->db->getLastQuery()); + } + + public function testLastQueryReturnsValidStatmentNoArgs(): void + { + $this->stubOutMethodsToAllowCleanCompletion(false); + $query = "SELECT BLAH FROM BLAH"; + + $this->db->pq($query, array()); + + $this->assertEquals($query, $this->db->getLastQuery()); + } + + public function testLastQueryReturnsValidStatmentUsingArgs(): void + { + $this->stubOutMethodsToAllowCleanCompletion(false); + $query = "SELECT BLAH FROM BLAH WHERE (p.personid=:1 OR php.proposalid=:2 OR lc.proposalid=:3)"; + $args = array('one', 'two', 'three'); + + $this->db->pq($query, $args); + + $this->assertEquals("SELECT BLAH FROM BLAH WHERE (p.personid='one' OR php.proposalid='two' OR lc.proposalid='three')", $this->db->getLastQuery()); + } + + public function testIdReturnsConnInsertId(): void + { + $insertId = $this->getFunctionMock(__NAMESPACE__, "mysqli_insert_id"); + $insertId->expects($this->any())->willReturn(666); + + $this->assertEquals(666, $this->db->id()); + } +} \ No newline at end of file diff --git a/api/tests/Model/Services/AssignDataTest.php b/api/tests/Model/Services/AssignDataTest.php new file mode 100644 index 000000000..297474e2e --- /dev/null +++ b/api/tests/Model/Services/AssignDataTest.php @@ -0,0 +1,141 @@ +getMockBuilder(mysqli::class) + ->onlyMethods(['prepare']) + ->getMock(); + $this->db = new MySQL($connStub); + + $this->assignData = new AssignData($this->db); + $this->stmtStub = $this->getMockBuilder(\mysqli_stmt::class) + ->disableOriginalConstructor() + ->onlyMethods(['bind_param', 'execute', 'get_result', 'close']) + ->getMock(); + + // can only stub this out once + if (!$this->insertId) { + $this->insertId = $this->getFunctionMock('SynchWeb\Database\Type', "mysqli_insert_id"); + $this->insertId->expects($this->any())->willReturn(666); + } + + $this->stmtStub->method('execute')->willReturn(true); + $connStub->method('prepare')->willReturn($this->stmtStub); + } + + public function testGetContainerCreatesCorrectSql(): void + { + $this->assignData->getContainer('testVisitId', 'testContainerId'); + $this->assertEquals("SELECT d.dewarid,bl.beamlinename,c.containerid,c.code FROM Container c INNER JOIN Dewar d ON d.dewarid = c.dewarid INNER JOIN Shipping s ON s.shippingid = d.shippingid INNER JOIN BLSession bl ON bl.proposalid = s.proposalid INNER JOIN Proposal p ON s.proposalid = p.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) LIKE 'testVisitId' AND c.containerid='testContainerId'", $this->db->getLastQuery()); + } + + public function testAssignContainerCreatesCorrectSql(): void + { + $container = array('CONTAINERID' => 'testContainerId', 'BEAMLINENAME' => 'testBeamLineName', 'DEWARID' => 'testDewarId', 'CODE' => 'testCode'); + $this->assignData->assignContainer($container, "testLocation"); + $this->assertEquals("UPDATE Dewar SET dewarstatus='processing' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUnassignContainerCreatesCorrectSql(): void + { + $container = array('CONTAINERID' => 'testContainerId', 'BEAMLINENAME' => 'testBeamLineName', 'DEWARID' => 'testDewarId', 'CODE' => 'testCode'); + $this->assignData->unassignContainer($container); + $this->assertEquals("INSERT INTO ContainerHistory (containerid, status, location, beamlinename) VALUES ('testContainerId','at facility','','')", $this->db->getLastQuery()); + } + + public function testUpdateContainerAndHistoryCreatesCorrectSql(): void + { + $this->assignData->updateContainerAndHistory('testContainerId', 'testStatus', 'testBeamlineName', 'testLocation'); + $this->assertEquals("INSERT INTO ContainerHistory (containerid, status, location, beamlinename) VALUES ('testContainerId','testStatus','testLocation','testBeamlineName')", $this->db->getLastQuery()); + } + + public function testUpdateContainerCreatesCorrectSql(): void + { + $this->assignData->updateContainer('testContainerId', 'testStatus', 'testBeamlineName', 'testLocation'); + $this->assertEquals("UPDATE Container SET beamlinelocation='testBeamlineName', samplechangerlocation='testLocation', containerstatus='testStatus' WHERE containerid='testContainerId'", $this->db->getLastQuery()); + } + + public function testUpdateContainerHistoryCreatesCorrectSql(): void + { + $this->assignData->updateContainerHistory('testContainerId', 'testStatus', 'testBeamlineName', 'testLocation'); + $this->assertEquals("INSERT INTO ContainerHistory (containerid, status, location, beamlinename) VALUES ('testContainerId','testStatus','testLocation','testBeamlineName')", $this->db->getLastQuery()); + } + + public function testGetDewarWithVisitDataCreatesCorrectSql(): void + { + $this->assignData->getDewar('testDewarId', 'testProposalId', 'testVisitId'); + $this->assertEquals("SELECT d.dewarid FROM Dewar d INNER JOIN Shipping s ON s.shippingid = d.shippingid INNER JOIN BLSession bl ON bl.proposalid = s.proposalid INNER JOIN Proposal p ON s.proposalid = p.proposalid WHERE CONCAT(p.proposalcode, p.proposalnumber, '-', bl.visit_number) LIKE 'testVisitId' AND d.dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testGetDewarWithNoVisitDataCreatesCorrectSql(): void + { + $this->assignData->getDewar('testDewarId', 'testProposalId', null); + $this->assertEquals("SELECT d.dewarid FROM Dewar d INNER JOIN Shipping s ON s.shippingid = d.shippingid INNER JOIN BLSession bl ON bl.proposalid = s.proposalid INNER JOIN Proposal p ON s.proposalid = p.proposalid WHERE p.proposalid='testProposalId' AND d.dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUpdateDewarCreatesCorrectSql(): void + { + $this->assignData->updateDewar('testDewarId', 'testStatus'); + $this->assertEquals("UPDATE Dewar SET dewarstatus='testStatus' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUpdateDewarWithUnprocessingStatusCreatesCorrectSql(): void + { + $this->assignData->updateDewar('testDewarId', 'unprocessing'); + $this->assertEquals("UPDATE Dewar SET dewarstatus='at facility' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testDeactivateDewarCreatesCorrectSql(): void + { + TestUtils::mockDBReturnsResult($this->stmtStub, [['STORAGELOCATION'=> "current location"], ]); + $this->assignData->deactivateDewar('testDewarId'); + $this->assertEquals("SELECT containerid FROM Container WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUpdateDewarHistoryCreatesCorrectSql(): void + { + $this->assignData->updateDewarHistory('testDewarId', 'testStatus'); + $this->assertEquals("UPDATE Dewar SET dewarstatus='testStatus' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUpdateDewarHistoryWithBeamlineDataCreatesCorrectSql(): void + { + $this->assignData->updateDewarHistory('testDewarId', 'testStatus', 'testBeamline'); + $this->assertEquals("UPDATE Dewar SET dewarstatus='testStatus' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testUpdateDewarHistoryWithBeamlineDataAndStatusCreatesCorrectSql(): void + { + $this->assignData->updateDewarHistory('testDewarId', 'testStatus', 'testBeamline', 'testStatus'); + $this->assertEquals("UPDATE Dewar SET dewarstatus='testStatus' WHERE dewarid='testDewarId'", $this->db->getLastQuery()); + } + + public function testGetContainerBarcodesForProposalCreatesCorrectSql(): void + { + $this->assignData->getContainerBarcodesForProposal('testProposalId'); + $this->assertEquals("SELECT cr.barcode FROM ContainerRegistry cr INNER JOIN ContainerRegistry_has_Proposal crhp ON crhp.containerregistryid = cr.containerregistryid WHERE crhp.proposalid = 'testProposalId'", $this->db->getLastQuery()); + } +} diff --git a/api/tests/Model/Services/BaseUserDataTestCase.php b/api/tests/Model/Services/BaseUserDataTestCase.php new file mode 100644 index 000000000..33b3f30b4 --- /dev/null +++ b/api/tests/Model/Services/BaseUserDataTestCase.php @@ -0,0 +1,60 @@ +getMockBuilder(mysqli::class) + ->onlyMethods(['prepare']) + ->getMock(); + $this->db = new MySQL($connStub); + + $this->userData = new UserData($this->db); + $stmtStub = $this->getMockBuilder(\mysqli_stmt::class) + ->disableOriginalConstructor() + ->onlyMethods(['bind_param', 'execute', 'get_result', 'close']) + ->getMock(); + + // can only stub this out once + if (!$this->insertId) + { + $this->insertId = $this->getFunctionMock('SynchWeb\Database\Type', "mysqli_insert_id"); + $this->insertId->expects($class->any())->willReturn(666); + } + + $this->query = []; + $stmtStub->expects($this->exactly($invocationNumber))->method('execute') + ->willReturnCallback([$this, "recordEntry"]); + $connStub->expects($this->exactly($invocationNumber))->method('prepare')->willReturn($stmtStub); + } + + public function recordEntry() + { + array_push($this->query, $this->db->getLastQuery()); + return true; + } +} diff --git a/api/tests/Model/Services/UserDataSingleInvocationTest.php b/api/tests/Model/Services/UserDataSingleInvocationTest.php new file mode 100644 index 000000000..d5a64bb86 --- /dev/null +++ b/api/tests/Model/Services/UserDataSingleInvocationTest.php @@ -0,0 +1,343 @@ +setUpMocks(1); + } + + public function testAddGroup(): void + { + $id = $this->userData->addGroup("testGroup"); + $this->assertEquals("INSERT INTO UserGroup (name) VALUES ('testGroup')", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testUpdateGroup(): void + { + $this->userData->updateGroup(1, "testGroup"); + $this->assertEquals("UPDATE UserGroup SET name='testGroup' WHERE usergroupid=1", $this->db->getLastQuery()); + } + + public function testGetGroupsWithGidSpecified(): void + { + $this->userData->getGroups(1); + $this->assertEquals("SELECT g.usergroupid, g.name, count(uhp.personid) as users FROM UserGroup g LEFT OUTER JOIN UserGroup_has_Person uhp ON uhp.usergroupid = g.usergroupid WHERE g.usergroupid=1 GROUP BY g.usergroupid, g.name ORDER BY g.name", $this->db->getLastQuery()); + } + + public function testGetGroupsWithGidUnspecified(): void + { + $this->userData->getGroups(); + $this->assertEquals("SELECT g.usergroupid, g.name, count(uhp.personid) as users FROM UserGroup g LEFT OUTER JOIN UserGroup_has_Person uhp ON uhp.usergroupid = g.usergroupid GROUP BY g.usergroupid, g.name ORDER BY g.name", $this->db->getLastQuery()); + } + + public function testGetPermissionsWithDefaults(): void + { + $this->userData->getPermissions(); + $this->assertEquals("SELECT p.permissionid, p.type, p.description FROM Permission p WHERE 1=1 ORDER BY p.type LIMIT 0,15", $this->db->getLastQuery()); + } + + public function testGetPermissionsCountWithDefaults(): void + { + $this->userData->getPermissions(true); + $this->assertEquals("SELECT count(p.permissionid) as tot FROM Permission p WHERE 1=1 ", $this->db->getLastQuery()); + } + + public function testGetPermissionsCountWithVals(): void + { + $this->userData->getPermissions(true, 'ss', 11, 22, 10, 2); + $this->assertEquals("SELECT count(p.permissionid) as tot FROM Permission p INNER JOIN UserGroup_has_Permission uhp ON uhp.permissionid = p.permissionid WHERE 1=1 AND uhp.usergroupid=11 AND p.permissionid=22 AND (lower(p.type) LIKE lower(CONCAT(CONCAT('%','ss'),'%')))", $this->db->getLastQuery()); + } + + public function testGetPermissionsWithVals(): void + { + $this->userData->getPermissions(false, 'ss', 11, 22, 10, 20); + $this->assertEquals("SELECT p.permissionid, p.type, p.description FROM Permission p INNER JOIN UserGroup_has_Permission uhp ON uhp.permissionid = p.permissionid WHERE 1=1 AND uhp.usergroupid=11 AND p.permissionid=22 AND (lower(p.type) LIKE lower(CONCAT(CONCAT('%','ss'),'%'))) ORDER BY p.type LIMIT 190,10", $this->db->getLastQuery()); + } + + public function testAddGroupPermission(): void + { + $id = $this->userData->addGroupPermission(11, 22); + $this->assertEquals("INSERT INTO UserGroup_has_Permission (usergroupid, permissionid) VALUES (11,22)", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testAddGroupUser(): void + { + $id = $this->userData->addGroupUser(11, 22); + $this->assertEquals("INSERT INTO UserGroup_has_Person (usergroupid, personid) VALUES (22,11)", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testAddPermission(): void + { + $id = $this->userData->addPermission('a test type', 'a new test permission'); + $this->assertEquals("INSERT INTO Permission (type,description) VALUES ('a test type','a new test permission')", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testAddUserWithEmail(): void + { + $id = $this->userData->addUser('testLoginId', 'Bob', 'Tester', 'test@tester.com'); + $this->assertEquals("INSERT INTO Person (login, givenname, familyname, emailaddress) VALUES ('testLoginId', 'Bob', 'Tester', 'test@tester.com')", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testAddUserWithoutEmail(): void + { + $id = $this->userData->addUser('testLoginId', 'Bob', 'Tester'); + $this->assertEquals("INSERT INTO Person (login, givenname, familyname, emailaddress) VALUES ('testLoginId', 'Bob', 'Tester', null)", $this->db->getLastQuery()); + $this->assertEquals(666, $id); + } + + public function testCheckLogin(): void + { + $res = $this->userData->checkLogin('testLoginId'); + $this->assertEquals("SELECT login FROM Person WHERE login='testLoginId'", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetLaboratory(): void + { + $res = $this->userData->getLaboratory(1); + $this->assertEquals("SELECT l.name, l.address, l.city, l.postcode, l.country FROM Laboratory l WHERE l.laboratoryid=1", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUser(): void + { + $res = $this->userData->getUser(123, 1, 2); + $this->assertEquals("SELECT p.personid, p.laboratoryid FROM Person p LEFT OUTER JOIN ProposalHasPerson php ON php.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE (p.personid=123 OR php.proposalid=1 OR lc.proposalid=1) AND p.personid=2", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithDefaults(): void + { + $res = $this->userData->getUsers(true, true, 's', 3); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithDefaults(): void + { + $res = $this->userData->getUsers(false, true, 's', 3); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.familyname,p.givenname LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithInvalidSortBy(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'blah'); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBy(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN'); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithInvalidSortBy(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'blah'); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.familyname,p.givenname LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortBy(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN'); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE p.login IS NOT NULL AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypID(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE p.login IS NOT NULL AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPid(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE p.login IS NOT NULL AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonId(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonId(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManager(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManager(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=null) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserId(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserId(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdGid(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, 8); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid INNER JOIN UserGroup_has_Person uhp ON uhp.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND uhp.usergroupid=8 GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdGid(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true, 8); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=8) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdSid(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, 9); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid INNER JOIN blsession_has_person shp ON shp.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND shp.sessionid=9 GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdSid(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, 9); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid INNER JOIN blsession_has_person shp ON shp.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND shp.sessionid=9", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdPjid(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, null, 10); + $this->assertEquals("SELECT CONCAT(p.personid, '-', php.projectid) as ppid, p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid INNER JOIN Project_has_Person php ON p.personid = php.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND php.projectid=10 GROUP BY p.personid ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdPjId(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, null, 10); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid INNER JOIN Project_has_Person php ON p.personid = php.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND php.projectid=10", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdVisitname(): void + { + $res = $this->userData->getUsers(false, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, null, null, 'visit1'); + $this->assertEquals("SELECT count(ses.sessionid) as visits, DATE_FORMAT(max(ses.startdate), '%d-%m-%Y') as last, shp.remote, shp.role, p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid INNER JOIN Session_has_Person shp ON shp.personid = p.personid INNER JOIN BLSession s ON shp.sessionid = s.sessionid INNER JOIN Proposal pr ON pr.proposalid = s.proposalid LEFT OUTER JOIN Session_has_Person shp2 ON p.personid = shp2.personid LEFT OUTER JOIN BLSession ses ON ses.sessionid = shp2.sessionid AND ses.startdate < s.startdate WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND shp.remote IS NOT NULL AND CONCAT(pr.proposalcode, pr.proposalnumber, '-', s.visit_number) LIKE 'visit1' GROUP BY p.personid, p.givenname, p.familyname, p.login ORDER BY p.login ASC LIMIT 30,15", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdVisitname(): void + { + $res = $this->userData->getUsers(true, true, 's', 3, 'LOGIN', 5, 5, 6, true, 7, null, null, null, 'visit1'); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid INNER JOIN Session_has_Person shp ON shp.personid = p.personid INNER JOIN BLSession s ON shp.sessionid = s.sessionid INNER JOIN Proposal pr ON pr.proposalid = s.proposalid LEFT OUTER JOIN Session_has_Person shp2 ON p.personid = shp2.personid LEFT OUTER JOIN BLSession ses ON ses.sessionid = shp2.sessionid AND ses.startdate < s.startdate WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) AND shp.remote IS NOT NULL AND CONCAT(pr.proposalcode, pr.proposalnumber, '-', s.visit_number) LIKE 'visit1'", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdPerPage(): void + { + $res = $this->userData->getUsers(false, true, 's', 1, 'LOGIN', 5, 5, 6, true, 7, null, null, null, null, 3); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login ASC LIMIT 0,3", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdPerPage(): void + { + $res = $this->userData->getUsers(true, true, 's', 1, 'LOGIN', 5, 5, 6, true, 7, null, null, null, null, 3); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersCountWithValidSortByPidPersonIdIsManagerCurrentUserIdPerPageDir(): void + { + $res = $this->userData->getUsers(true, true, 's', 1, 'LOGIN', 5, 5, 6, true, 7, null, null, null, null, 3, false); + $this->assertEquals("SELECT count(distinct p.personid) as tot FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid WHERE 1=1 AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%')))", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testGetUsersWithValidSortBypIDPersonIdIsManagerCurrentUserIdPerPageDirAndIsOnlyLogin(): void + { + $res = $this->userData->getUsers(false, true, 's', 1, 'LOGIN', 5, 5, 6, true, 7, null, null, null, null, 3, false, false, true); + $this->assertEquals("SELECT p.personid, p.givenname, p.familyname, CONCAT(p.givenname, ' ', p.familyname) as fullname, p.login, p.emailaddress, p.phonenumber, l.name as labname, l.address, l.city, l.postcode, l.country FROM Person p LEFT OUTER JOIN ProposalHasPerson prhp ON prhp.personid = p.personid LEFT OUTER JOIN LabContact lc ON lc.personid = p.personid LEFT OUTER JOIN Laboratory l ON l.laboratoryid = p.laboratoryid WHERE p.login IS NOT NULL AND (prhp.proposalid=5 OR lc.proposalid=5 OR p.personid=7) AND p.personid=6 AND (lower(p.familyname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.givenname) LIKE lower(CONCAT(CONCAT('%','s'),'%')) OR lower(p.login) LIKE lower(CONCAT(CONCAT('%','s'),'%'))) GROUP BY p.personid ORDER BY p.login DESC LIMIT 0,3", $this->db->getLastQuery()); + $this->assertEmpty($res); + } + + public function testRemoveGroupPermission(): void + { + $this->userData->removeGroupPermission(1, 2); + $this->assertEquals("DELETE FROM UserGroup_has_Permission WHERE usergroupid=1 and permissionid=2", $this->db->getLastQuery()); + } + + public function testUpdateUserAllFields(): void + { + $this->userData->updateUser(123, 'Bob', 'Tester', '641 5231', 'test@tester.com'); + $this->assertEquals("UPDATE Person SET FAMILYNAME='Bob', GIVENNAME='Tester', PHONENUMBER='641 5231', EMAILADDRESS='test@tester.com' WHERE personid=123", $this->db->getLastQuery()); + } + + public function testUpdateUserNumberField(): void + { + $this->userData->updateUser(123, Null, Null, '641 5231', Null); + $this->assertEquals("UPDATE Person SET PHONENUMBER='641 5231' WHERE personid=123", $this->db->getLastQuery()); + } + + public function testUpdateLaboratoryLabSpecified(): void + { + $this->userData->updateLaboratory(123, 'lab name', 'lab address', 'city', 'postcode', 'country', 321); + $this->assertEquals("UPDATE Laboratory SET name='lab name', address='lab address', city='city', postcode='postcode', country='country' WHERE laboratoryid=321", $this->db->getLastQuery()); + } + + public function testRemoveGropuUser(): void + { + $this->userData->removeGroupUser(1, 2); + $this->assertEquals("DELETE FROM UserGroup_has_Person WHERE usergroupid=2 and personid=1", $this->db->getLastQuery()); + } + + public function testUpdatePermission(): void + { + $this->userData->updatePermission(1, 'mx', 'perm description'); + } +} diff --git a/api/tests/Model/Services/UserDataTest.php b/api/tests/Model/Services/UserDataTest.php new file mode 100644 index 000000000..4fed32361 --- /dev/null +++ b/api/tests/Model/Services/UserDataTest.php @@ -0,0 +1,38 @@ +setUpMocks(2); + + $this->userData->updateLaboratory(123, 'lab name', 'lab address', 'city', 'postcode', 'country'); + + $this->assertEquals("INSERT INTO Laboratory ( name, address, city, postcode, country ) VALUES ( 'lab name', 'lab address', 'city', 'postcode', 'country' )", $this->query[0]); + $this->assertEquals("UPDATE Person SET laboratoryid=666 WHERE personid=123", $this->db->getLastQuery()); + } + + public function testUpdateLaboratoryNoLabSpecifiedOnlyOneFieldSpecifiedThenJustThatFieldInInsert(): void + { + $this->setUpMocks(2); + + $this->userData->updateLaboratory(123, 'lab name', null, null, null, null); + + $this->assertEquals("INSERT INTO Laboratory ( name ) VALUES ( 'lab name' )", $this->query[0]); + $this->assertEquals("UPDATE Person SET laboratoryid=666 WHERE personid=123", $this->query[1]); + } +} diff --git a/api/tests/Model/UserTest.php b/api/tests/Model/UserTest.php new file mode 100644 index 000000000..5bc075f5e --- /dev/null +++ b/api/tests/Model/UserTest.php @@ -0,0 +1,165 @@ +assertEquals($userId, $user->loginId); + $this->assertEquals($personId, $user->personId); + $this->assertEquals($givenName, $user->givenName); + $this->assertEquals($familyName, $user->familyName); + } + + public function testUserHasNoAdminTypeWhenNotSet(): void + { + $perm1 = "read"; + $perm2 = "write"; + + $user = new User("blah", 231312, "frod", "blegs", array($perm1, $perm2), array(), array()); + + $this->assertEmpty($user->getAdminType()); + } + + public function testUserHasCorrectAdminTypeWhenNotSet(): void + { + $perm1 = "read_admin"; + $perm2 = "write"; + + $user = new User("blah", 231312, "frod", "blegs", array($perm1, $perm2), array(), array()); + + $this->assertEquals("read", ($user->getAdminType())); + } + + public function testUserHasExpectedPermissions(): void + { + $perm1 = "read"; + $perm2 = "write"; + + $user = new User("blah", 231312, "frod", "blegs", array($perm1, $perm2), array(), array()); + + $this->assertTrue($user->hasPermission($perm1)); + $this->assertTrue($user->hasPermission($perm2)); + } + + public function testUserDoesNotHaveUnexpectedPermissions(): void + { + $perm1 = "read"; + $perm2 = "write"; + + $user = new User("blah", 231312, "frod", "blegs", array($perm1, $perm2), array(), array()); + + $this->assertFalse($user->hasPermission("cigar")); + $this->assertFalse($user->hasPermission(null)); + $this->assertFalse($user->hasPermission(array())); + } + + public function testUserCanWithoutExpectedPermissionsHaltsRequest(): void + { + $perm1 = "read"; + $perm2 = "write"; + + $user = new User("blah", 231312, "frod", "blegs", array($perm1, $perm2), array(), array()); + $appStub = $this->getMockBuilder(Slim::class) + ->disableOriginalConstructor() + ->onlyMethods(['halt']) + ->getMock(); + $appStub->expects($this->once()) + ->method('halt') + ->with(403, $this->anything()); + + $user->can("bogus", $appStub); + } + + public function testUserIsInExceptectedGroups(): void + { + $group1 = "abba"; + $group2 = "foals"; + + $user = new User("blah", 231312, "frod", "blegs", array(), array($group1, $group2), array()); + + $this->assertTrue($user->isInGroup($group1)); + $this->assertTrue($user->isInGroup($group2)); + } + + public function testUserIsNotInUnexceptectedGroups(): void + { + $group1 = "abba"; + $group2 = "foals"; + + $user = new User("blah", 231312, "frod", "blegs", array(), array($group1, $group2), array()); + + $this->assertFalse($user->isInGroup("the buggles")); + $this->assertFalse($user->isInGroup(null)); + $this->assertFalse($user->isInGroup(array())); + } + + public function testCacheRejectsUnexpectedKeys(): void + { + $key1 = "sdf"; + $val1 = "found me"; + $user = new User("blah", 231312, "frod", "blegs", array(), array(), array()); + $this->assertNull($user->getFromCache($key1)); + $user->setInCache($key1, $val1); + $this->assertNull($user->getFromCache($key1)); + } + + public function testCacheWorksAsExpected(): void + { + $key1 = "shipment"; + $val1 = "found me"; + $val2 = "and again"; + $user = new User("blah", 231312, "frod", "blegs", array(), array(), array()); + $this->assertNull($user->getFromCache($key1)); + $user->setInCache($key1, $val1); + $this->assertEquals($val1, $user->getFromCache($key1)); + } + + public function testCacheAllowsValuesToBeUpdated(): void + { + $key1 = "shipment"; + $val1 = "found me"; + $val2 = "and again"; + $user = new User("blah", 231312, "frod", "blegs", array(), array(), array()); + $this->assertNull($user->getFromCache($key1)); + $user->setInCache($key1, $val1); + $this->assertEquals($val1, $user->getFromCache($key1)); + $user->setInCache($key1, $val2); + $this->assertEquals($val2, $user->getFromCache($key1)); + } + + public function testCacheAllowsMultipeValues(): void + { + $key1 = "shipment"; + $val1 = "found me"; + $key2 = "container"; + $val2 = "and again"; + $user = new User("blah", 231312, "frod", "blegs", array(), array(), array()); + $this->assertNull($user->getFromCache($key1)); + $user->setInCache($key1, $val1); + $this->assertEquals($val1, $user->getFromCache($key1)); + $user->setInCache($key2, $val2); + $this->assertEquals($val2, $user->getFromCache($key2)); + $this->assertEquals($val1, $user->getFromCache($key1)); + } +} \ No newline at end of file diff --git a/api/tests/Page/DownloadTest.php b/api/tests/Page/DownloadTest.php new file mode 100644 index 000000000..cc4028e5d --- /dev/null +++ b/api/tests/Page/DownloadTest.php @@ -0,0 +1,100 @@ +downloadStub = $this->createPartialMock(Download::class, []); + + $this->mocked_file_test = sys_get_temp_dir() . "/mocked_files"; + $this->mocked_files = [ + [ + "FILEPATH" => $this->mocked_file_test, + "FILENAME" => "file1.txt", + "FILECONTENT" => 'This is file 1.' + ], + [ + "FILEPATH" => $this->mocked_file_test, + "FILENAME" => "file2.txt", + "FILECONTENT" => 'This is file 2.' + ], + ]; + + foreach ($this->mocked_files as $mocked_file) { + $this->write_file($mocked_file["FILEPATH"], $mocked_file["FILENAME"], $mocked_file["FILECONTENT"]); + } + + $this->tempFile = tempnam(sys_get_temp_dir(), 'zip'); + } + + function tearDown(): void + { + // Clean up the temporary file. + unlink($this->tempFile); + + // Delete mocked files + $files = glob($this->mocked_file_test . '/*'); + foreach ($files as $file) { + if (is_file($file)) { + unlink($file); + } + } + + rmdir($this->mocked_file_test); + } + + /** + * @runInSeparateProcess + */ + public function testDownload() + { + + ob_start(); + $this->downloadStub->_streamZipFile($this->mocked_files, "finalZip"); + ob_get_clean(); + + file_put_contents($this->tempFile, $this->getActualOutput()); + + $zip = new ZipArchive(); + $result = $zip->open($this->tempFile); + + $this->assertTrue($result, 'Failed to open the zip file, return should be true.'); + + $this->assertEquals(2, $zip->numFiles, 'The zip file does not contain the expected number of files.'); + + foreach ($this->mocked_files as $mocked_file) { + $filename = $mocked_file['FILENAME']; + $fileContents = $zip->getFromName($filename); + $this->assertNotNull($fileContents, $filename . ' not found in the zip file.'); + $this->assertEquals($mocked_file['FILECONTENT'], $fileContents, 'The contents of ' . $filename . ' are not as expected.'); + } + + $zip->close(); + + } + +} diff --git a/api/tests/Page/StatsTest.php b/api/tests/Page/StatsTest.php new file mode 100644 index 000000000..427c07a9b --- /dev/null +++ b/api/tests/Page/StatsTest.php @@ -0,0 +1,60 @@ +statsStub = $this->getMockBuilder(Stats::class) + ->disableOriginalConstructor() + ->onlyMethods(['_data_collections']) + ->getMock(); + } + + public function testBeamlineCallWithDCTypeInvokesDataCollectionCorrectly(): void + { + $this->statsStub->args = array('t' => 'dc'); + $this->statsStub->expects($this->once()) + ->method('_data_collections') + ->with(False, True); + $this->statsStub->_beamline(); + } + + public function testBeamlineCallForScreeningsInvokesDataCollectionCorrectly(): void + { + $this->statsStub->args = array('t' => 'sc'); + $this->statsStub->expects($this->once()) + ->method('_data_collections') + ->with(True); + $this->statsStub->_beamline(); + } + + public function testBeamlineCallForFCTypeInvokesDataCollectionCorrectly(): void + { + $this->statsStub->args = array('t' => 'fc'); + $this->statsStub->expects($this->once()) + ->method('_data_collections') + ->with(False, False); + $this->statsStub->_beamline(); + } + + public function testBeamlineCallForUnknownTypeCallsError(): void + { + $statsStub1 = $this->getMockBuilder(Stats::class) + ->disableOriginalConstructor() + ->onlyMethods(['_error']) + ->getMock(); + $statsStub1->args = array('t' => 'invalidtype'); + $statsStub1->expects($this->once()) + ->method('_error') + ->with('No such stat type'); + $statsStub1->_beamline(); + } +} diff --git a/api/tests/PageTest.php b/api/tests/PageTest.php new file mode 100644 index 000000000..6eea660f1 --- /dev/null +++ b/api/tests/PageTest.php @@ -0,0 +1,35 @@ +getMockBuilder(Slim::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + $dbStub = $this->getMockBuilder(MySQL::class) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + + + $this->expectException(\Exception::class); + $page = new Page($slimStub, $dbStub, null); + + $page->arg('invald_key'); + } +} \ No newline at end of file diff --git a/api/tests/TestConfig.php b/api/tests/TestConfig.php new file mode 100644 index 000000000..6481cd278 --- /dev/null +++ b/api/tests/TestConfig.php @@ -0,0 +1,10 @@ + 'user', 'pass' => 'pass', 'db' => 'localhost/ispyb', 'port' => '80'); + $dbtype = 'mysql'; + + +?> diff --git a/api/tests/TestUtils.php b/api/tests/TestUtils.php new file mode 100644 index 000000000..5e4883891 --- /dev/null +++ b/api/tests/TestUtils.php @@ -0,0 +1,47 @@ +method('get_result')->willReturn(new MockTestResult($returnRows)); + } + +} + +/** + * Class to help with returning mock results from sql + */ +class MockTestResult +{ + public $num_rows; + private $returnRows; + private $row_index = -1; + + function __construct($returnRows){ + $this->returnRows = $returnRows; + $this->num_rows = $returnRows? count($returnRows) : 0; + } + + function fetch_assoc() { + + $this->row_index++; + if ($this->row_index < $this->num_rows) { + return $this->returnRows[$this->row_index]; + } + return null; + + } +} diff --git a/api/tests/phpunit.xml b/api/tests/phpunit.xml new file mode 100644 index 000000000..06310fe35 --- /dev/null +++ b/api/tests/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ../src + + + + + + + + + . + + + + \ No newline at end of file diff --git a/catalog-info.yaml b/catalog-info.yaml new file mode 100644 index 000000000..dd9c2c59c --- /dev/null +++ b/catalog-info.yaml @@ -0,0 +1,17 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: SynchWeb + description: Experimental Information Management for Synchrotrons and Large Scale Facilities. This is not ISPyB which is the database + annotations: + github.com/project-slug: DiamondLightSource/SynchWeb + links: + - url: https://ispyb.diamond.ac.uk + title: Live system +spec: + type: website/service + lifecycle: production + owner: group:lims + dependsOn: + - component:dials-rest + - resource:ispyb-database diff --git a/client/.eslintrc-vue.js b/client/.eslintrc-vue.js new file mode 100644 index 000000000..5b1262985 --- /dev/null +++ b/client/.eslintrc-vue.js @@ -0,0 +1,16 @@ +module.exports = { + extends: [ + // add more generic rulesets here, such as: + 'plugin:vue/recommended' // Vue.js 2.x rules + ], + "ignorePatterns": ["temp.js", "**/vendor/**"], + rules: { + // override/add rules settings here, such as: + // 'vue/no-unused-vars': 'error' + 'vue/require-default-prop': 0, // this is common throughout - should probably be fixed but pollutes output substantially so disabling for now + 'vue/multi-word-component-names': 0 // ditto - though less important - this will require renaming lots of components to protect against quite a minor issue + }, + env: { + amd: true // registers globals for define and require + }, +} \ No newline at end of file diff --git a/client/.eslintrc.js b/client/.eslintrc.js new file mode 100644 index 000000000..bb0fd06ac --- /dev/null +++ b/client/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + extends: [ + 'eslint:recommended', + "plugin:backbone/recommended" + ], + "ignorePatterns": ["temp.js", "**/vendor/**", "**/src/js/app/**", "**/**/*vue*"], + rules: { + // override/add rules settings here, such as: + 'no-unused-vars': 0, // mostly flags unused vars in function signatures - which could be tidied up, but are quite prevalent so not worth the effort + 'no-undef': 0, // picks up lots of backbone defaults + 'backbone/model-defaults': 0, // widely abused issue - so unlikely to be addressed without considerable effort + 'backbone/collection-model': 0, // ditto + 'backbone/defaults-on-top': 0, // ditto + 'backbone/initialize-on-top': 0 // ditto + }, + env: { + es6: true, + amd: true // registers globals for define and require + }, + parserOptions: { + "ecmaVersion": 11, + "sourceType": "module" + } +} \ No newline at end of file diff --git a/client/babel.config.js b/client/babel.config.js new file mode 100644 index 000000000..721e8b825 --- /dev/null +++ b/client/babel.config.js @@ -0,0 +1 @@ +module.exports = { presets: ["@babel/preset-env"] }; diff --git a/client/package-lock.json b/client/package-lock.json index d42f37736..932359ebf 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,4768 +1,24537 @@ { "name": "synchweb-webpack", - "version": "1.0.0", - "lockfileVersion": 1, + "version": "1.1.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "3d-view": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.0.tgz", - "integrity": "sha1-gxrpQtdQjFCAHj4G+v4ejFdOF74=", - "requires": { - "matrix-camera-controller": "^2.1.1", - "orbit-camera-controller": "^4.0.0", - "turntable-camera-controller": "^3.0.0" + "packages": { + "": { + "name": "synchweb-webpack", + "version": "1.1.0", + "license": "Apache-2.0", + "dependencies": { + "@highcharts/map-collection": "^1.1.2", + "backbone": "1.1.2", + "backbone-validation": "0.9.1", + "backbone.marionette": "2.1.0", + "backbone.paginator": "2.0.0", + "backbone.syphon": "0.5.0", + "backgrid": "0.3.8", + "backgrid-paginator": "0.3.9", + "backgrid-select-all": "0.3.5", + "ci": "^2.2.0", + "csv-file-validator": "^1.7.4", + "d3-color": "^2.0.0", + "d3-scale": "^3.2.3", + "d3-scale-chromatic": "^2.0.0", + "d3-selection": "^2.0.0", + "date-fns": "^2.16.1", + "date-fns-tz": "^1.0.12", + "flot-axislabels": "^1.0.0", + "flot-pie": "^1.0.0", + "font-awesome": "^4.2.0", + "highcharts": "^7.2.2", + "jquery": "^1.12.4", + "jquery-color": "^3.0.0-alpha.1", + "jquery-flot-resize": "^1.0.0", + "jquery-touchswipe": "^1.6.19", + "jquery-ui": "^1.12.1", + "jquery-ui-timepicker-addon": "^1.6.3", + "jquery.cookie": "^1.4.1", + "jquery.flot": "^0.8.3", + "jquery.flot.tooltip": "^0.9.0", + "lodash-es": "^4.17.21", + "luxon": "^1.25.0", + "markdown": "^0.5.0", + "plotly.js": "^1.52.2", + "portal-vue": "2.1.7", + "promise": "^8.0.3", + "tailwindcss": "^1.9.5", + "three": "^0.143.0", + "uglymol": "^0.6.4", + "underscore": "1.8.3", + "util": "^0.12.4", + "vee-validate": "^2.2.15", + "vue": "^2.6.10", + "vue-router": "^3.4.3", + "vuex": "^3.5.1", + "zlibjs": "^0.1.7" + }, + "devDependencies": { + "@babel/preset-env": "^7.21.5", + "@vue/test-utils": "^1.3.5", + "@vue/vue2-jest": "^28.1.0", + "@webpack-cli/serve": "^1.7.0", + "autoprefixer": "^10.0.2", + "copy-webpack-plugin": "^6.4.1", + "css-loader": "^5.2.7", + "css-minimizer-webpack-plugin": "^4.1.0", + "eslint": "^7.32.0", + "eslint-loader": "^4.0.2", + "eslint-plugin-backbone": "^2.1.1", + "eslint-plugin-vue": "^9.5.1", + "file-loader": "^6.2.0", + "html-loader": "^0.5.5", + "html-webpack-plugin": "^5.5.0", + "imports-loader": "^0.8.0", + "jest-environment-jsdom": "^29.5.0", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.1.14", + "postcss-color-function": "^4.1.0", + "postcss-extend-rule": "^3.0.0", + "postcss-import": "^13.0.0", + "postcss-loader": "^4.0.4", + "postcss-mixins": "^7.0.1", + "postcss-nested": "^5.0.1", + "postcss-scss": "^3.0.4", + "postcss-simple-vars": "^6.0.1", + "postcss-strip-inline-comments": "^0.1.5", + "postcss-utilities": "^0.8.4", + "raw-loader": "^4.0.2", + "underscore-template-loader": "^1.0.0", + "url-loader": "^4.1.1", + "vue-loader": "^15.10.0", + "vue-template-compiler": "^2.6.10", + "webpack": "^5.74.0", + "webpack-bundle-analyzer": "^4.6.1", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.11.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "@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==", + "node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "node_modules/@babel/compat-data": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "engines": { + "node": ">=6.9.0" } }, - "@choojs/findup": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", - "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", - "requires": { - "commander": "^2.15.1" + "node_modules/@babel/core": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@fullhuman/postcss-purgecss": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", - "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", - "requires": { - "postcss": "7.0.32", - "purgecss": "^2.3.0" + "node_modules/@babel/generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, "dependencies": { - "postcss": { - "version": "7.0.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", - "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@highcharts/map-collection": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@highcharts/map-collection/-/map-collection-1.1.3.tgz", - "integrity": "sha512-Kb31dEQc7Kf7dxkmJEUjYlZ2wtRftEumJR/bq3SwvcviZeEhz+t6vMLXqS9qxfeO0UzSOaTCLiNMBsg/W6d7/g==" + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@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=", - "requires": { - "wgs84": "0.0.0" + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@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==", - "requires": { - "@mapbox/geojson-area": "0.2.2", - "concat-stream": "~1.6.0", - "minimist": "1.2.0", - "sharkdown": "^0.1.0" + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", + "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "semver": "^6.3.0" }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz", + "integrity": "sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A==", + "dev": true, "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@mapbox/geojson-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", - "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==" + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz", + "integrity": "sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } }, - "@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", - "integrity": "sha1-zlblOfg1UrWNENZy6k1vya3HsjQ=" + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@mapbox/mapbox-gl-supported": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", - "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==" + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@mapbox/point-geometry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", - "integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI=" + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@mapbox/tiny-sdf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz", - "integrity": "sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg==" + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@mapbox/unitbezier": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", - "integrity": "sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4=" + "node_modules/@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@mapbox/vector-tile": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", - "requires": { - "@mapbox/point-geometry": "~0.1.0" + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" + "engines": { + "node": ">=6.9.0" } }, - "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", + "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", + "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@plotly/d3-sankey": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz", - "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==", - "requires": { - "d3-array": "1", - "d3-collection": "1", - "d3-shape": "^1.2.0" + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@plotly/d3-sankey-circular": { - "version": "0.33.1", - "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", - "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", - "requires": { - "d3-array": "^1.2.1", - "d3-collection": "^1.0.4", - "d3-shape": "^1.2.0", - "elementary-circuits-directed-graph": "^1.0.4" + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@turf/area": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.0.1.tgz", - "integrity": "sha512-Zv+3N1ep9P5JvR0YOYagLANyapGWQBh8atdeR3bKpWcigVXFsEKNUw03U/5xnh+cKzm7yozHD6MFJkqQv55y0g==", - "requires": { - "@turf/helpers": "6.x", - "@turf/meta": "6.x" + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@turf/bbox": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.0.1.tgz", - "integrity": "sha512-EGgaRLettBG25Iyx7VyUINsPpVj1x3nFQFiGS3ER8KCI1MximzNLsam3eXRabqQDjyAKyAE1bJ4EZEpGvspQxw==", - "requires": { - "@turf/helpers": "6.x", - "@turf/meta": "6.x" + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "@turf/centroid": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.0.2.tgz", - "integrity": "sha512-auyDauOtC4eddH7GC3CHFTDu2PKhpSeKCRhwhHhXtJqn2dWCJQNIoCeJRmfXRIbzCWhWvgvQafvvhq8HNvmvWw==", - "requires": { - "@turf/helpers": "6.x", - "@turf/meta": "6.x" + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "@turf/helpers": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.4.tgz", - "integrity": "sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g==" + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@turf/meta": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz", - "integrity": "sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA==", - "requires": { - "@turf/helpers": "6.x" + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", + "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true + "node_modules/@babel/helpers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true + "node_modules/@babel/parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "@types/node": { - "version": "13.7.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.4.tgz", - "integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==", - "dev": true + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } }, - "@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 + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", - "dev": true - }, - "@vue/component-compiler-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.1.tgz", - "integrity": "sha512-+lN3nsfJJDGMNz7fCpcoYIORrXo0K3OTsdr8jCM7FuqdI4+70TY6gxY6viJ2Xi1clqyPg7LpeOWwjF31vSMmUw==", + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "dev": true, - "requires": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.14", - "postcss-selector-parser": "^6.0.2", - "prettier": "^1.18.2", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "requires": { - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "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==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "a-big-triangle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz", - "integrity": "sha1-7v0wsCqPUl6LH3K7a7GwwWdRx5Q=", - "requires": { - "gl-buffer": "^2.1.1", - "gl-vao": "^1.2.0", - "weak-map": "^1.0.5" + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "abs-svg-path": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", - "integrity": "sha1-32Acjo0roQ1KdtYl4japo5wnI78=" + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.5.tgz", + "integrity": "sha512-gGOEvFzm3fWoyD5uZq7vVTD57pPJ3PczPUD/xCFGjzBpUosnklmXyKnGQbbbGs1NPNPskFex0j93yKbHt0cHyg==", "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==" + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "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==", - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "dev": true, "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - }, - "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==" - } + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" } }, - "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz", + "integrity": "sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "add-line-numbers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz", - "integrity": "sha1-SNu96kfb0jTer+rGyTzqb3C0t+M=", - "requires": { - "pad-left": "^1.0.2" + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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=", - "requires": { - "robust-orientation": "^1.1.3" + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "almost-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/almost-equal/-/almost-equal-1.1.0.tgz", - "integrity": "sha1-+FHGMROHV5lCdqou++jfowZszN0=" - }, - "alpha-complex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/alpha-complex/-/alpha-complex-1.0.0.tgz", - "integrity": "sha1-kIZYcNawVCrnPAwTHU75iWabctI=", - "requires": { - "circumradius": "^1.0.0", - "delaunay-triangulate": "^1.1.6" + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "alpha-shape": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz", - "integrity": "sha1-yDEJkj7P2mZ9IWP+Tyb+JHJvZKk=", - "requires": { - "alpha-complex": "^1.0.0", - "simplicial-complex-boundary": "^1.0.0" + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "optional": true - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "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==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "ansicolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", - "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "dev": true, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "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==" + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-bounds": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz", - "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==" + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-normalize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", - "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", - "requires": { - "array-bounds": "^1.0.0" + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "array-range": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz", - "integrity": "sha1-9W5GWRhDYRxqVvd+8C7afFAIm/w=" + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-rearrange": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz", - "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==" + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dev": true, - "requires": { - "array-uniq": "^1.0.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz", + "integrity": "sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dev": true, "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "ast-types": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", - "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", - "dev": true + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dev": true, - "requires": { - "lodash": "^4.17.14" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "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==", - "dev": true - }, - "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==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "atob-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz", - "integrity": "sha1-uI3KYAaSK5YglPdVaCa6sxxKKWs=" - }, - "autoprefixer": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz", - "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dev": true, - "requires": { - "browserslist": "^4.16.3", - "caniuse-lite": "^1.0.30001196", - "colorette": "^1.2.2", - "fraction.js": "^4.0.13", - "normalize-range": "^0.1.2", - "postcss-value-parser": "^4.1.0" - }, "dependencies": { - "browserslist": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz", - "integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001208", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.712", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "caniuse-lite": { - "version": "1.0.30001208", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", - "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.712", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz", - "integrity": "sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==", - "dev": true - }, - "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", - "dev": true - }, - "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==", - "dev": true - } - } - }, - "backbone": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.1.2.tgz", - "integrity": "sha1-wsBMZr+HJo+4LBd6zr7/fTe6by0=", - "requires": { - "underscore": ">=1.5.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone-validation": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/backbone-validation/-/backbone-validation-0.9.1.tgz", - "integrity": "sha1-TPKP0uHSNuRUSDtOtTQqjs4Wq8E=", - "requires": { - "backbone": ">=1.0.0", - "underscore": ">=1.4.3" + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone.babysitter": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/backbone.babysitter/-/backbone.babysitter-0.1.12.tgz", - "integrity": "sha1-fKlGQ07u+94aVTYFx0twSbbfr8E=", - "requires": { - "backbone": ">=0.9.9 <=1.3.x", - "underscore": ">=1.4.0 <=1.8.3" + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone.marionette": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/backbone.marionette/-/backbone.marionette-2.1.0.tgz", - "integrity": "sha1-yyYzRByrXns7XW5T9DrjVDiq4Ho=", - "requires": { - "backbone": "1.0.0 - 1.1.2", - "backbone.babysitter": "^0.1.0", - "backbone.wreqr": "^1.0.0", - "underscore": "1.4.4 - 1.6.0" + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone.paginator": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.0.tgz", - "integrity": "sha1-wPZ7kOcGWZuAWEzWcnTvZSvgOoU=", - "requires": { - "backbone": "^1.1.2", - "underscore": "^1.5.0" + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone.syphon": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/backbone.syphon/-/backbone.syphon-0.5.0.tgz", - "integrity": "sha1-FoyhIORdI0KiAjR8vuYAhKf1SUg=", - "requires": { - "backbone": "^1.1.0", - "jquery": "^1.8.0 || ^2.1.0", - "underscore": "^1.6.0" + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backbone.wreqr": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/backbone.wreqr/-/backbone.wreqr-1.4.0.tgz", - "integrity": "sha1-doIDDJqvCQ7Nhzsh2/SFAWk7JpY=", - "requires": { - "backbone": ">=0.9.9 <=1.3.x", - "underscore": ">=1.3.3 <=1.8.3" + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "backgrid": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/backgrid/-/backgrid-0.3.5.tgz", - "integrity": "sha1-igmXcu/b1c8XDGyQXMqLKj/ZhNI=", - "requires": { - "backbone": "~1.1.0", - "underscore": "~1.5.2" - }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dev": true, "dependencies": { - "underscore": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz", - "integrity": "sha1-EzXF5PXm0zu7SwBrqMhqAPVW3gg=" - } + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "backgrid-paginator": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/backgrid-paginator/-/backgrid-paginator-0.3.5.tgz", - "integrity": "sha1-cnl1ixI4qwMFasLySBIPOqZ7elo=" - }, - "backgrid-select-all": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/backgrid-select-all/-/backgrid-select-all-0.3.5.tgz", - "integrity": "sha1-FDqADl2V/yrlqE14v0+6QflIHpQ=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "barycentric": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz", - "integrity": "sha1-8VYruJGyb0/sRjqC7to2V4AOxog=", - "requires": { - "robust-linear-solve": "^1.0.0" + "node_modules/@babel/preset-env": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.5.tgz", + "integrity": "sha512-fj06hw89dpiZzGZtxn+QybifF07nNiZjZ7sazs2aVDcysAZVGjW7+7iFYxg6GLNM47R/thYfLdrXc+2f11Vi9A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.5", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.5", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.3", + "babel-plugin-polyfill-corejs3": "^0.8.1", + "babel-plugin-polyfill-regenerator": "^0.5.0", + "core-js-compat": "^3.30.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dev": true, - "requires": { - "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" - }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "@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" } }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", "dev": true }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true + "node_modules/@babel/runtime": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } }, - "bfj": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", - "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, - "requires": { - "bluebird": "^3.5.5", - "check-types": "^8.0.3", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" } }, - "big-rat": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz", - "integrity": "sha1-do0JO7V5MN0Y7Vdcf8on3FORreo=", - "requires": { - "bit-twiddle": "^1.0.2", - "bn.js": "^4.11.6", - "double-bits": "^1.1.1" + "node_modules/@babel/traverse": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "binary-search-bounds": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.4.tgz", - "integrity": "sha512-2hg5kgdKql5ClF2ErBcSx0U5bnl5hgS4v7wMnLFodyR47yMtj2w+UAZB+0CiqyHct2q543i7Bi4/aMIegorCCg==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "node_modules/@babel/types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "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/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true }, - "bitmap-sdf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.3.tgz", - "integrity": "sha512-ojYySSvWTx21cbgntR942zgEgqj38wHctN64vr4vYRFf3GKVmI23YlA94meWGkFslidwLwGCsMy2laJ3g/94Sg==", - "requires": { - "clamp": "^1.0.1" + "node_modules/@choojs/findup": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", + "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", + "dependencies": { + "commander": "^2.15.1" + }, + "bin": { + "findup": "bin/findup.js" } }, - "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" } }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, "dependencies": { - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - } + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - } + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "boundary-cells": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/boundary-cells/-/boundary-cells-2.0.1.tgz", - "integrity": "sha1-6QWo0UGc9Hyza+Pb9SXbXiTeAEI=", - "requires": { - "tape": "^4.0.0" + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "box-intersect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/box-intersect/-/box-intersect-1.0.2.tgz", - "integrity": "sha512-yJeMwlmFPG1gIa7Rs/cGXeI6iOj6Qz5MG5PE61xLKpElUGzmJ4abm+qsLpzxKJFpsSDq742BQEocr8dI2t8Nxw==", - "requires": { - "bit-twiddle": "^1.0.2", - "typedarray-pool": "^1.1.0" + "node_modules/@fullhuman/postcss-purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", + "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", + "dependencies": { + "postcss": "7.0.32", + "purgecss": "^2.3.0" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/@fullhuman/postcss-purgecss/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" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "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" + "node_modules/@fullhuman/postcss-purgecss/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/@fullhuman/postcss-purgecss/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/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@highcharts/map-collection": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@highcharts/map-collection/-/map-collection-1.1.4.tgz", + "integrity": "sha512-JzoBLWFJSjzjUV/m+eU2FbJBeQCfheyFJBUyQ9xyUhSs8yqUbE/JN0x9VZe7lDr71MfTO+bO50YpCrk5BTRCzA==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "requires": { - "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" + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "peer": true, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "node_modules/@jest/console/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==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "node_modules/@jest/console/node_modules/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": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", + "node_modules/@jest/console/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==", "dev": true, - "requires": { - "pako": "~1.0.5" + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "browserslist": { - "version": "4.8.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.7.tgz", - "integrity": "sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA==", + "node_modules/@jest/console/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==", "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001027", - "electron-to-chromium": "^1.3.349", - "node-releases": "^1.1.49" + "peer": true + }, + "node_modules/@jest/console/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" } }, - "buble": { - "version": "0.19.8", - "resolved": "https://registry.npmjs.org/buble/-/buble-0.19.8.tgz", - "integrity": "sha512-IoGZzrUTY5fKXVkgGHw3QeXFMUNBFv+9l8a4QJKG1JhG3nCMHTdEX1DCOg8568E2Q9qvAQIiSokv6Jsgx8p2cA==", - "requires": { - "acorn": "^6.1.1", - "acorn-dynamic-import": "^4.0.0", - "acorn-jsx": "^5.0.1", - "chalk": "^2.4.2", - "magic-string": "^0.25.3", - "minimist": "^1.2.0", - "os-homedir": "^2.0.0", - "regexpu-core": "^4.5.4" + "node_modules/@jest/console/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "peer": true, "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "os-homedir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-2.0.0.tgz", - "integrity": "sha512-saRNz0DSC5C/I++gFIaJTXoFJMRwiP5zHar5vV3xQ2TkgEw6hDCcU5F272JjUylpiVgBrZNQHnfjkLabTfb92Q==" + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "bubleify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bubleify/-/bubleify-1.2.1.tgz", - "integrity": "sha512-vp3NHmaQVoKaKWvi15FTMinPNjfp+47+/kFJ9ifezdMF/CBLArCxDVUh+FQE3qRxCRj1qyjJqilTBHHqlM8MaQ==", - "requires": { - "buble": "^0.19.3" + "node_modules/@jest/core/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/@jest/core/node_modules/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": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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/@jest/core/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true + "node_modules/@jest/core/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==", + "dev": true, + "peer": true }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true + "node_modules/@jest/core/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } }, - "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=", - "dev": true + "node_modules/@jest/core/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "node_modules/@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "node_modules/@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "peer": true, + "dependencies": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", "dev": true, - "requires": { - "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" + "peer": true, + "dependencies": { + "jest-get-type": "^28.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", "dev": true, - "requires": { - "callsites": "^2.0.0" + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", "dev": true, - "requires": { - "caller-callsite": "^2.0.0" + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true + "node_modules/@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "node_modules/@jest/reporters/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==", "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "camelcase-css": { + "node_modules/@jest/reporters/node_modules/color-convert": { "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==" - }, - "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==", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "caniuse-lite": { - "version": "1.0.30001030", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz", - "integrity": "sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw==", - "dev": true + "node_modules/@jest/reporters/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==", + "dev": true, + "peer": true }, - "canvas-fit": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", - "integrity": "sha1-rhO+Zq3kL1vg5IfjRfzjCl5bXl8=", - "requires": { - "element-size": "^1.1.1" + "node_modules/@jest/reporters/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" } }, - "cardinal": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz", - "integrity": "sha1-ylu2iltRG5D+k7ms6km97lwyv+I=", - "requires": { - "ansicolors": "~0.2.1", - "redeyed": "~0.4.0" + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "cdt2d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cdt2d/-/cdt2d-1.0.0.tgz", - "integrity": "sha1-TyEkNLzWe9s9aLj+9KzcLFRBUUE=", - "requires": { - "binary-search-bounds": "^2.0.3", - "robust-in-sphere": "^1.1.3", - "robust-orientation": "^1.1.3" - } - }, - "cell-orientation": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cell-orientation/-/cell-orientation-1.0.1.tgz", - "integrity": "sha1-tQStlqZq0obZ7dmFoiU9A7gNKFA=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "node_modules/@jest/reporters/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "node_modules/@jest/reporters/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==", + "dev": true, + "peer": true, "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "check-types": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", - "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", - "dev": true - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "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" + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "node_modules/@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", "dev": true, - "requires": { - "tslib": "^1.9.0" + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "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==", + "node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "peer": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "circumcenter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/circumcenter/-/circumcenter-1.0.0.tgz", - "integrity": "sha1-INeqE7F/usUvUtpPVMasi5Bu5Sk=", - "requires": { - "dup": "^1.0.0", - "robust-linear-solve": "^1.0.0" + "node_modules/@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "circumradius": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/circumradius/-/circumradius-1.0.0.tgz", - "integrity": "sha1-cGxEfj5VzR7T0RvRM+N8JSzDBbU=", - "requires": { - "circumcenter": "^1.0.0" + "node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "clamp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", - "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ=" - }, - "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==", + "node_modules/@jest/transform/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==", "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, + "peer": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "node_modules/@jest/transform/node_modules/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": { - "source-map": "~0.6.0" + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "clean-pslg": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz", - "integrity": "sha1-vTXHRgt+irWp92Gl7VF5aqPIbBE=", - "requires": { - "big-rat": "^1.0.3", - "box-intersect": "^1.0.1", - "nextafter": "^1.0.0", - "rat-vec": "^1.1.1", - "robust-segment-intersect": "^1.0.1", - "union-find": "^1.0.2", - "uniq": "^1.0.1" + "node_modules/@jest/transform/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true + "node_modules/@jest/transform/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==", + "dev": true, + "peer": true }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "node_modules/@jest/transform/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==", "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" + "peer": true, + "engines": { + "node": ">=8" } }, - "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 - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/@jest/transform/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==", "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "color-alpha": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz", - "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==", - "requires": { - "color-parse": "^1.3.8" + "node_modules/@jest/types/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", - "requires": { - "color-name": "1.1.3" + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "color-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz", - "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==", - "requires": { - "clamp": "^1.0.1" + "node_modules/@jest/types/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "node_modules/@jest/types/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==", + "dev": true, + "peer": true }, - "color-normalize": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz", - "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==", - "requires": { - "clamp": "^1.0.1", - "color-rgba": "^2.1.1", - "dtype": "^2.0.0" + "node_modules/@jest/types/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" } }, - "color-parse": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz", - "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==", - "requires": { - "color-name": "^1.0.0", - "defined": "^1.0.0", - "is-plain-obj": "^1.1.0" + "node_modules/@jest/types/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "color-rgba": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.1.1.tgz", - "integrity": "sha512-VaX97wsqrMwLSOR6H7rU1Doa2zyVdmShabKrPEIFywLlHoibgD3QW9Dw6fSqM4+H/LfjprDNAUUW31qEQcGzNw==", - "requires": { - "clamp": "^1.0.1", - "color-parse": "^1.3.8", - "color-space": "^1.14.6" + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "color-space": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.16.0.tgz", - "integrity": "sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg==", - "requires": { - "hsluv": "^0.0.3", - "mumath": "^3.3.4" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" } }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" } }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + "node_modules/@jridgewell/source-map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } }, - "colormap": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.1.tgz", - "integrity": "sha512-TEzNlo/qYp6pBoR2SK9JiV+DG1cmUcVO/+DEJqVPSHIKNlWh5L5L4FYog7b/h0bAnhKhpOAvx/c1dFp2QE9sFw==", - "requires": { - "lerp": "^1.0.3" + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, - "compare-angle": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/compare-angle/-/compare-angle-1.0.1.tgz", - "integrity": "sha1-pOtjQW6jx0f8a9bItjZotN5PoSk=", - "requires": { - "robust-orientation": "^1.0.2", - "robust-product": "^1.0.0", - "robust-sum": "^1.0.0", - "signum": "^0.0.0", - "two-sum": "^1.0.0" + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" } }, - "compare-cell": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/compare-cell/-/compare-cell-1.0.0.tgz", - "integrity": "sha1-qetwj24OQa73qlZrEw8ZaNyeGqo=" + "node_modules/@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==" }, - "compare-oriented-cell": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz", - "integrity": "sha1-ahSf7vnfxPj8YjWOUd1C7/u9w54=", - "requires": { - "cell-orientation": "^1.0.1", - "compare-cell": "^1.0.0" + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" } }, - "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==", - "dev": true + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", + "peerDependencies": { + "mapbox-gl": ">=0.32.1 <2.0.0" + } }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", + "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" } }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "engines": { + "node": ">=6.0.0" + } + }, + "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==", "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - } + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "compute-dims": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/compute-dims/-/compute-dims-1.1.0.tgz", - "integrity": "sha512-YHMiIKjH/8Eom8zATk3g8/lH3HxGCZcVQyEfEoVrfWI7od/WRpTgRGShnei3jArYSx77mQqPxZNokjGHCdLfxg==", - "requires": { - "utils-copy": "^1.0.0", - "validate.io-array": "^1.0.6", - "validate.io-matrix-like": "^1.0.2", - "validate.io-ndarray-like": "^1.0.0", - "validate.io-positive-integer": "^1.0.0" + "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==", + "dev": true, + "engines": { + "node": ">= 8" } }, - "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/@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==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" } }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true + "node_modules/@npmcli/fs/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" + } }, - "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/@npmcli/fs/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/fs/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 }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, - "requires": { - "bluebird": "^3.1.1" + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" } }, - "const-max-uint32": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/const-max-uint32/-/const-max-uint32-1.0.2.tgz", - "integrity": "sha1-8Am7YjDmeO2HTdLWqc2ePL+rtnY=" + "node_modules/@plotly/d3-sankey": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz", + "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==", + "dependencies": { + "d3-array": "1", + "d3-collection": "1", + "d3-shape": "^1.2.0" + } }, - "const-pinf-float64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/const-pinf-float64/-/const-pinf-float64-1.0.0.tgz", - "integrity": "sha1-9u+w15+cCYbT558pI6v5twtj1yY=" + "node_modules/@plotly/d3-sankey-circular": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", + "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", + "dependencies": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.4", + "d3-shape": "^1.2.0", + "elementary-circuits-directed-graph": "^1.0.4" + } }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "node_modules/@plotly/d3-sankey-circular/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==" }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" + "node_modules/@plotly/d3-sankey/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/@plotly/point-cluster": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@plotly/point-cluster/-/point-cluster-3.1.9.tgz", + "integrity": "sha512-MwaI6g9scKf68Orpr1pHZ597pYx9uP8UEFXLPbsCmuw3a84obwz6pnMXGc90VhgDNeNiLEdlmuK7CPo+5PIxXw==", + "dependencies": { + "array-bounds": "^1.0.1", + "binary-search-bounds": "^2.0.4", + "clamp": "^1.0.1", + "defined": "^1.0.0", + "dtype": "^2.0.0", + "flatten-vertex-data": "^1.0.2", + "is-obj": "^1.0.1", + "math-log2": "^1.0.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0" } }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, - "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=", - "requires": { - "affine-hull": "^1.0.0", - "incremental-convex-hull": "^1.0.1", - "monotone-convex-hull-2d": "^1.0.1" + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true, + "peer": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-detect": "4.0.8" } }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "peer": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "engines": { + "node": ">=10.13.0" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "node_modules/@turf/area": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz", + "integrity": "sha512-xCZdiuojokLbQ+29qR6qoMD89hv+JAgWjLrwSEWL+3JV8IXKeNFl6XkEJz9HGkVpnXvQKJoRz4/liT+8ZZ5Jyg==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } }, - "copy-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", - "dev": true, - "requires": { - "cacache": "^12.0.3", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.1", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "webpack-log": "^2.0.0" + "node_modules/@turf/bbox": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", + "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" } }, - "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/@turf/centroid": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz", + "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "node_modules/@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", + "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", + "dependencies": { + "@turf/helpers": "^6.5.0" }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "peer": true, "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "country-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", - "integrity": "sha1-UcMz3N8Sknt+XuucEKyBEqYSCJY=" + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "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==", + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "peer": true, + "dependencies": { + "@babel/types": "^7.20.7" } }, - "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==", + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", "dev": true, - "requires": { - "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" + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "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==", + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", "dev": true, - "requires": { - "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" + "dependencies": { + "@types/node": "*" } }, - "css-color-function": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", - "integrity": "sha1-jtJMLAIFBzM5+voAS8jBQfzLKC4=", + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", "dev": true, - "requires": { - "balanced-match": "0.1.0", - "color": "^0.11.0", - "debug": "^3.1.0", - "rgb": "~0.1.0" - }, "dependencies": { - "balanced-match": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.1.0.tgz", - "integrity": "sha1-tQS9BYabOSWd0MXvw12EMXbczEo=", - "dev": true - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "^1.0.0" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } + "@types/node": "*" } }, - "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=", - "dev": true - }, - "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==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - }, "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "css-font": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz", - "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==", - "requires": { - "css-font-size-keywords": "^1.0.0", - "css-font-stretch-keywords": "^1.0.1", - "css-font-style-keywords": "^1.0.1", - "css-font-weight-keywords": "^1.0.0", - "css-global-keywords": "^1.0.1", - "css-system-font-keywords": "^1.0.0", - "pick-by-alias": "^1.2.0", - "string-split-by": "^1.0.0", - "unquote": "^1.1.0" + "node_modules/@types/eslint": { + "version": "8.40.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", + "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" } }, - "css-font-size-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz", - "integrity": "sha1-hUh1rOmspqjS7g00WkSq6btttss=" - }, - "css-font-stretch-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz", - "integrity": "sha1-UM7puboDH7XJUtRyMTnx4Qe1SxA=" + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } }, - "css-font-style-keywords": { + "node_modules/@types/estree": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz", - "integrity": "sha1-XDUygT9jtKHelU0TzqhqtDM0CeQ=" - }, - "css-font-weight-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz", - "integrity": "sha1-m8BGcayFvHJLV07106yWsNYE/Zc=" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true }, - "css-global-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz", - "integrity": "sha1-cqmupyeW0Bmx0qMlLeTlqqN+Smk=" + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } }, - "css-loader": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", - "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dev": true, - "requires": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.23", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.1.1", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.0.2", - "schema-utils": "^2.6.0" - }, "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "schema-utils": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", - "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1" - } - } + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "peer": true, + "dependencies": { + "@types/node": "*" } }, - "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/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, - "css-system-font-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", - "integrity": "sha1-hcbwhquk6zLFcaMIav/ENLhII+0=" - }, - "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==", + "node_modules/@types/http-proxy": { + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", "dev": true, - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" + "dependencies": { + "@types/node": "*" } }, - "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==" - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, - "csscolorparser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", - "integrity": "sha1-s085HupNqPPpgjHizNjfnAQfFxs=" - }, - "cssesc": { + "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - }, "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "@types/istanbul-lib-report": "*" } }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "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.2", - "postcss-unique-selectors": "^4.0.1" - }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" } }, - "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=", + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", "dev": true }, - "cssnano-util-get-match": { + "node_modules/@types/parse-json": { "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=", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "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==", + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "peer": true }, - "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==", + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, - "csso": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.2.tgz", - "integrity": "sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.37" - } + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, - "csv-file-validator": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/csv-file-validator/-/csv-file-validator-1.8.0.tgz", - "integrity": "sha512-+/wdJxbe9zk1KJv7GC5aCVOVrg10W7xWIypILuQsJ3ocegF/YueTarb8Dqg1snEfkPmh2aCjbhVXnu1gM3RRIA==", - "requires": { - "famulus": "2.1.2", - "lodash": "4.17.15", - "papaparse": "^5.2.0" - } + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true }, - "cubic-hermite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz", - "integrity": "sha1-hOOy8nKzFFToOTuZu2rtRRaMFOU=" + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } }, - "cwise": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/cwise/-/cwise-1.0.10.tgz", - "integrity": "sha1-JO7mBy69/WuMb12tsXCQtkmxK+8=", - "requires": { - "cwise-compiler": "^1.1.1", - "cwise-parser": "^1.0.0", - "static-module": "^1.0.0", - "uglify-js": "^2.6.0" - }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } + "@types/express": "*" } }, - "cwise-compiler": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", - "integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=", - "requires": { - "uniq": "^1.0.0" + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" } }, - "cwise-parser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", - "integrity": "sha1-jkk8F9VPl8sDCp6YVLyGyd+zVP4=", - "requires": { - "esprima": "^1.0.3", - "uniq": "^1.0.0" - }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, "dependencies": { - "esprima": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", - "integrity": "sha1-CZNQL+r2aBODJXVvMPmlH+7sEek=" - } + "@types/node": "*" } }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "dev": true }, - "d3": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", - "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" + "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 }, - "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/@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + "node_modules/@types/ws": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", + "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, - "d3-color": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.0.tgz", - "integrity": "sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg==" + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } }, - "d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, - "d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", - "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "node_modules/@vue/compiler-sfc": { + "version": "2.7.14", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", + "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", + "dependencies": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" } }, - "d3-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", - "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" - }, - "d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + "node_modules/@vue/compiler-sfc/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" + } }, - "d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", - "requires": { - "d3-color": "1" + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" } }, - "d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } }, - "d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true }, - "d3-scale": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", - "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", - "requires": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "^2.1.1", - "d3-time-format": "2 - 3" - }, + "node_modules/@vue/component-compiler-utils/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, "dependencies": { - "d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "requires": { - "internmap": "^1.0.0" - } - } + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "d3-scale-chromatic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz", - "integrity": "sha512-LLqy7dJSL8yDy7NRmf6xSlsFZ6zYvJ4BcWFE4zBrOPnQERv9zj24ohnXKRbyi9YHnYV+HN1oEO3iFK971/gkzA==", - "requires": { - "d3-color": "1 - 2", - "d3-interpolate": "1 - 2" + "node_modules/@vue/component-compiler-utils/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "d3-selection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", - "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==" + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true }, - "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "requires": { - "d3-path": "1" + "node_modules/@vue/test-utils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.3.6.tgz", + "integrity": "sha512-udMmmF1ts3zwxUJEIAj5ziioR900reDrt6C9H3XpWPsLBx2lpHKoA4BTdd9HNIYbkGltWw+JjWJ+5O6QBwiyEw==", + "dev": true, + "dependencies": { + "dom-event-types": "^1.0.0", + "lodash": "^4.17.15", + "pretty": "^2.0.0" + }, + "peerDependencies": { + "vue": "2.x", + "vue-template-compiler": "^2.x" } }, - "d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", - "requires": { - "d3-array": "2" - }, + "node_modules/@vue/vue2-jest": { + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/@vue/vue2-jest/-/vue2-jest-28.1.0.tgz", + "integrity": "sha512-4aZB6tRQw7x5Xi0dt1itLfOv4WeNLutccV7FHw47xfHjFr/JnwgqTmzC9yOiYzC9H7T2/3DSW95Bzl3RETtFlA==", + "dev": true, "dependencies": { - "d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "requires": { - "internmap": "^1.0.0" - } + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "@vue/component-compiler-utils": "^3.1.0", + "chalk": "^2.1.0", + "css-tree": "^2.0.1", + "source-map": "0.5.6", + "tsconfig": "^7.0.0" + }, + "engines": { + "node": ">10" + }, + "peerDependencies": { + "@babel/core": "7.x", + "babel-jest": ">= 28 < 29", + "jest": "28.x", + "typescript": ">= 4.3", + "vue": "^2.x", + "vue-template-compiler": "^2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "d3-time-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", - "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", - "requires": { - "d3-time": "1 - 2" + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" - }, - "date-fns": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", - "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==" + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, - "date-fns-tz": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.0.12.tgz", - "integrity": "sha512-Ca+9pjGkU90XDHnclfSjz9o7g/ZqyYyYI0aCYmbf65P75oy8gktuaRslO3UPXl3ADgAnF9/KCykQkpU3/xvtWQ==" + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, - "requires": { - "ms": "2.0.0" + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "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=", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, - "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==", - "requires": { - "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" + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, - "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/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" + "dependencies": { + "@xtuc/long": "4.2.2" } }, - "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==", - "requires": { - "object-keys": "^1.0.12" + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, "dependencies": { - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - } + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "delaunay-triangulate": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/delaunay-triangulate/-/delaunay-triangulate-1.1.6.tgz", - "integrity": "sha1-W7yiGweBmNS8PHV5ajXLuYwllUw=", - "requires": { - "incremental-convex-hull": "^1.0.1", - "uniq": "^1.0.1" + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, - "detect-kerning": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz", - "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==" - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "detective": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", - "requires": { - "acorn-node": "^1.6.1", - "defined": "^1.0.0", - "minimist": "^1.1.1" - }, + "node_modules/3d-view": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.1.tgz", + "integrity": "sha512-YSLRHXNpSziaaiK2R0pI5+JKguoJVbtWmIv9YyBFtl0+q42kQwJB/JUulbFR/1zYFm58ifjKQ6kVdgZ6tyKtCA==", "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - } - } - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "matrix-camera-controller": "^2.1.1", + "orbit-camera-controller": "^4.0.0", + "turntable-camera-controller": "^3.0.0" } }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" + "node_modules/a-big-triangle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz", + "integrity": "sha512-AboEtoSPueZisde3Vr+7VRSfUIWBSGZUOtW3bJrOZXgIyK7dNNDdpDmOKJjg5GmJLlRKUONWV8lMgTK8MBhQWw==", + "dependencies": { + "gl-buffer": "^2.1.1", + "gl-vao": "^1.2.0", + "weak-map": "^1.0.5" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" + "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" } }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, - "requires": { - "utila": "~0.4" + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "node_modules/acorn-globals/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" + "bin": { + "acorn": "bin/acorn" }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true - } + "engines": { + "node": ">=0.4.0" } }, - "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==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "requires": { - "domelementtype": "1" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "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" } }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" + "node_modules/acorn-node/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" } }, - "dotignore": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", - "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", - "requires": { - "minimatch": "^3.0.4" + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, - "double-bits": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz", - "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY=" + "node_modules/add-line-numbers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz", + "integrity": "sha512-w+2a1malCvWwACQFBpZ5/uwmHGaGYT+aGIxA8ONF5vlhe6X/gD3eR8qVoLWa+5nnWAOq2LuPbrqDYqj1pn0WMg==", + "dependencies": { + "pad-left": "^1.0.2" + } }, - "draw-svg-path": { + "node_modules/affine-hull": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz", - "integrity": "sha1-bxFtli3TFLmepTTW9Y3WbNvWk3k=", - "requires": { - "abs-svg-path": "~0.1.1", - "normalize-svg-path": "~0.1.0" + "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", + "integrity": "sha512-3QNG6+vFAwJvSZHsJYDJ/mt1Cxx9n5ffA+1Ohmj7udw0JuRgUVIXK0P9N9pCMuEdS3jCNt8GFX5q2fChq+GO3Q==", + "dependencies": { + "robust-orientation": "^1.1.3" } }, - "dtype": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", - "integrity": "sha1-zQUjI84GFETs0uj1dI9popvihDQ=" - }, - "dup": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", - "integrity": "sha1-UfxaxoX4GWRp3wuQXpNLIK9bQCk=" + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "requires": { - "readable-stream": "~1.1.9" + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true } } }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "earcut": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.2.tgz", - "integrity": "sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ==" - }, - "edges-to-adjacency-list": { + "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz", - "integrity": "sha1-wUbS4ISt37p0pRKTxuAZmkn3V/E=", - "requires": { - "uniq": "^1.0.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "ejs": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", - "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", - "dev": true + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } }, - "electron-to-chromium": { - "version": "1.3.360", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.360.tgz", - "integrity": "sha512-RE1pv2sjQiDRRN1nI0fJ0eQHZ9le4oobu16OArnwEUV5ycAU5SNjFyvzjZ1gPUAqBa2Ud1XagtW8j3ZXfHuQHA==", - "dev": true + "node_modules/almost-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/almost-equal/-/almost-equal-1.1.0.tgz", + "integrity": "sha512-0V/PkoculFl5+0Lp47JoxUcO0xSxhIBvm+BxHdD/OgXNmdRpRHCFnKVuUoWyS9EzQP+otSGv0m9Lb4yVkQBn2A==" }, - "element-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", - "integrity": "sha1-ZOXxWdlxIWMYRby67K8nnDm1404=" + "node_modules/alpha-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/alpha-complex/-/alpha-complex-1.0.0.tgz", + "integrity": "sha512-rhsjKfc9tMF5QZc0NhKz/zFzMu2rvHxCP/PyJtEmMkV7M848YjIoQGDlNGp+vTqxXjA8wAY2OxgR1K54C2Awkg==", + "dependencies": { + "circumradius": "^1.0.0", + "delaunay-triangulate": "^1.1.6" + } }, - "elementary-circuits-directed-graph": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.2.0.tgz", - "integrity": "sha512-eOQofnrNqebPtC29PvyNMGUBdMrIw5i8nOoC/2VOlSF84tf5+ZXnRkIk7TgdT22jFXK68CC7aA881KRmNYf/Pg==", - "requires": { - "strongly-connected-components": "^1.0.1" + "node_modules/alpha-shape": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz", + "integrity": "sha512-/V+fmmjtSA2yfQNq8iEqBxnPbjcOMXpM9Ny+yE/O7aLR7Q1oPzUc9bHH0fPHS3hUugUL/dHzTis6l3JirYOS/w==", + "dependencies": { + "alpha-complex": "^1.0.0", + "simplicial-complex-boundary": "^1.0.0" } }, - "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "engines": { + "node": ">=6" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "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==", - "requires": { - "once": "^1.4.0" + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" } }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } + "engines": { + "node": ">=8" } }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", - "dev": true + "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" + } }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { - "prr": "~1.0.1" + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "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==", + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } + "node_modules/array-bounds": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz", + "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==" }, - "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==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "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": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "engines": { + "node": ">=0.10.0" } }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "node_modules/array-normalize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", + "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", + "dependencies": { + "array-bounds": "^1.0.0" } }, - "es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + "node_modules/array-range": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz", + "integrity": "sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA==" }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } + "node_modules/array-rearrange": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz", + "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==" }, - "es6-templates": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", - "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "requires": { - "recast": "~0.11.12", - "through": "~2.3.6" + "engines": { + "node": ">=8" } }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==", + "dev": true, + "engines": { + "node": ">= 0.8" } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "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=" + "node_modules/atob-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz", + "integrity": "sha512-ArXcmHR/vwSN37HLVap/Y5SKpz12CuEybxe1sIYl7th/S6SQPrVMNFt6rblJzCOAxn0SHbXpknUtqbAIeo3Aow==" }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "node_modules/babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", "dev": true, - "requires": { - "estraverse": "^4.1.0" + "peer": true, + "dependencies": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "estraverse": { + "node_modules/babel-jest/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true + "node_modules/babel-jest/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true + "node_modules/babel-jest/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==", + "dev": true, + "peer": true }, - "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", - "dev": true + "node_modules/babel-jest/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "node_modules/babel-jest/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==", "dev": true, - "requires": { - "original": "^1.0.0" + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz", + "integrity": "sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==", + "dev": true, "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.4.0", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz", + "integrity": "sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==", "dev": true, - "requires": { - "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" + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.0", + "core-js-compat": "^3.30.1" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz", + "integrity": "sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "@babel/helper-define-polyfill-provider": "^0.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "node_modules/babel-preset-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/backbone": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.1.2.tgz", + "integrity": "sha512-bdiYFVF+mXQ3712Urje2uvYClLPXOd2jsvp8AHlPyMKqpHRPCmCxZf099kCRadhA2sMxp761XB2aZ0/HYF4fDg==", "dependencies": { - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - } + "underscore": ">=1.5.0" } }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, + "node_modules/backbone-validation": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/backbone-validation/-/backbone-validation-0.9.1.tgz", + "integrity": "sha512-bUdbJ8S3BvdLqxM8ndsaVXFNOa13W2ODCH0pQ9qYSRvFwEMehKn2edzeJgKyoS9trWcI0F94t7hpFL/7HzoY3A==", "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } + "backbone": ">=1.0.0", + "underscore": ">=1.4.3" } }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "node_modules/backbone.babysitter": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/backbone.babysitter/-/backbone.babysitter-0.1.12.tgz", + "integrity": "sha512-1zhTnN/QrnbUWrptAxKYL+ABEeyNPbr654PErBNxIYjKbktEcPeZz/dojcP3Id5PUCO/ldo334yTywNcpGl/oA==", "dependencies": { - "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==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "backbone": ">=0.9.9 <=1.3.x", + "underscore": ">=1.4.0 <=1.8.3" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "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" - }, + "node_modules/backbone.marionette": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/backbone.marionette/-/backbone.marionette-2.1.0.tgz", + "integrity": "sha512-04amIUEE9rWIQ6Do8TGHmTju6OByBxP/fkgsOtcmsDovDCUFPVLXJGEasbDFy1chL8xs4xix+ESrumd23/PR5Q==", "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "backbone": "1.0.0 - 1.1.2", + "backbone.babysitter": "^0.1.0", + "backbone.wreqr": "^1.0.0", + "underscore": "1.4.4 - 1.6.0" } }, - "extract-frustum-planes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz", - "integrity": "sha1-l9VwP/BWTIw8aDjKxF+ee8UsnvU=" + "node_modules/backbone.marionette/node_modules/underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==" }, - "falafel": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", - "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=", - "requires": { - "acorn": "^5.0.0", - "foreach": "^2.0.5", - "isarray": "0.0.1", - "object-keys": "^1.0.6" - }, + "node_modules/backbone.paginator": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.0.tgz", + "integrity": "sha512-v+KVUz3Jlf593ecIzor/BzuLczuhFYwJoAVKogQ6NGF+Q5BdExO3TczS5OD49WOi2pJ0rrYgBeAjAxkCuI+sdA==", "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - } + "backbone": "^1.1.2", + "underscore": "^1.5.0" } }, - "famulus": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/famulus/-/famulus-2.1.2.tgz", - "integrity": "sha512-UjfF9lOEP6IFLC/DTwUe5KbCYINbuYYJS+mivlnWyK8yqt/9WYHrJ4RihZ0pa9HVxQObu8IWroJOyyt8dXCVkw==", - "requires": { - "lodash": "^4.17.15" + "node_modules/backbone.syphon": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/backbone.syphon/-/backbone.syphon-0.5.0.tgz", + "integrity": "sha512-L5hlhyjbZsQZr7UE7UQBpxVl2iDLBNczTGlYDgqaK8EZaxm0jt+9qjVQxYXQ3TsThmpDk/gP5kkla5seiSn21g==", + "dependencies": { + "backbone": "^1.1.0", + "jquery": "^1.8.0 || ^2.1.0", + "underscore": "^1.6.0" } }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true + "node_modules/backbone.wreqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/backbone.wreqr/-/backbone.wreqr-1.4.0.tgz", + "integrity": "sha512-r5r71OxIRYiesUa6CNgD2zjChizsUz6rltthjMJeS+YTYTC7UlLvkEb9PMsBUrrbuoiZ9vXL2Znro94OlL7UfA==", + "dependencies": { + "backbone": ">=0.9.9 <=1.3.x", + "underscore": ">=1.3.3 <=1.8.3" + } }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, + "node_modules/backgrid": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/backgrid/-/backgrid-0.3.8.tgz", + "integrity": "sha512-Klzo941ahoj8Kqd0tRsau+VfXddV3YnQTwb6wVwIaaQxoJ9ORykQy2MNit1MUBnZO6IValYJPvCQyvZhnV6Lfg==", "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "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==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "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" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "backbone": "1.1.2 || 1.2.3 || ~1.3.2", + "underscore": "^1.8.0" } }, - "fast-isnumeric": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.3.tgz", - "integrity": "sha512-MdojHkfLx8pjRNZyGjOhX4HxNPaf0l5R/v5rGZ1bGXCnRPyQIUAe4I1H7QtrlUwuuiDHKdpQTjT3lmueVH2otw==", - "requires": { - "is-string-blank": "^1.0.1" + "node_modules/backgrid-paginator": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/backgrid-paginator/-/backgrid-paginator-0.3.9.tgz", + "integrity": "sha512-Y8nhpFoXZ4dk4eJ2wuwPeC1Uekq3H/0tLvZA48Ux+2tpMq3oPOlMv41EEghj6z1jWbvRAzwxuiyQXfQ2EpYEvA==", + "dependencies": { + "backbone": "1.1.2 || 1.2.3 || ~1.3.2", + "backbone.paginator": "^2.0.5", + "backgrid": "~0.3.7", + "underscore": "^1.8.0" } }, - "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==", - "dev": true + "node_modules/backgrid-paginator/node_modules/backbone.paginator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.8.tgz", + "integrity": "sha512-8XS2CTbjnwMbJ/3Traa1te2RPecOGbZ9tc52T89pzo6NXlVEJDFnC++dp7CQLBUZpgk3g0veX8mUbEF4wbD2NQ==", + "dependencies": { + "backbone": "1.1.2 || 1.2.3 || ^1.3.2", + "underscore": "^1.8.0" + } }, - "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/backgrid-select-all": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/backgrid-select-all/-/backgrid-select-all-0.3.5.tgz", + "integrity": "sha512-bwMQi5d8AnBSZDiV4nWrXcOSmEODbxB6/70mSHG8cGoDfjgW5X7mLiXlmlgEP3VsA1avFD6VvCvpAKZ4BS5f9Q==" }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "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/barycentric": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz", + "integrity": "sha512-47BuWXsenBbox4q1zqJrUoxq1oM1ysrYc5mdBACAwaP+CL+tcNauC3ybA0lzbIWzJCLZYMqebAx46EauTI2Nrg==", + "dependencies": { + "robust-linear-solve": "^1.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, - "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "node_modules/big-rat": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz", + "integrity": "sha512-AubEohDDrak6urvKkFMIlwPWyQbJ/eq04YsK/SNipH7NNiPCYchjQNvWYK5vyyMmtGXAmNmsAjIcfkaDuTtd8g==", + "dependencies": { + "bit-twiddle": "^1.0.2", + "bn.js": "^4.11.6", + "double-bits": "^1.1.1" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, - "requires": { - "reusify": "^1.0.4" + "engines": { + "node": "*" } }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "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==", "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" + "engines": { + "node": ">=8" } }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true + "node_modules/binary-search-bounds": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", + "integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==" }, - "file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", - "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", - "dev": true, - "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" - }, + "node_modules/bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==" + }, + "node_modules/bitmap-sdf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.4.tgz", + "integrity": "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg==" + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "dependencies": { - "schema-utils": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", - "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1" - } - } + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, - "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==", - "dev": true, - "optional": true - }, - "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "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/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "filtered-vector": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.4.tgz", - "integrity": "sha1-VkU8A030MC0pPKjs3qw/kKvGeNM=", - "requires": { - "binary-search-bounds": "^1.0.0", - "cubic-hermite": "^1.0.0" - }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "dependencies": { - "binary-search-bounds": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz", - "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k=" - } + "ms": "2.0.0" } }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/boundary-cells": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/boundary-cells/-/boundary-cells-2.0.2.tgz", + "integrity": "sha512-/S48oUFYEgZMNvdqC87iYRbLBAPHYijPRNrNpm/sS8u7ijIViKm/hrV3YD4sx/W68AsG5zLMyBEditVHApHU5w==" + }, + "node_modules/box-intersect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/box-intersect/-/box-intersect-1.0.2.tgz", + "integrity": "sha512-yJeMwlmFPG1gIa7Rs/cGXeI6iOj6Qz5MG5PE61xLKpElUGzmJ4abm+qsLpzxKJFpsSDq742BQEocr8dI2t8Nxw==", + "dependencies": { + "bit-twiddle": "^1.0.2", + "typedarray-pool": "^1.1.0" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "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==", "dev": true, - "requires": { - "locate-path": "^3.0.0" + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" } }, - "flatten-vertex-data": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", - "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==", - "requires": { - "dtype": "^2.0.0" + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" } }, - "flip-pixels": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz", - "integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA==" + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } }, - "flot-axislabels": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flot-axislabels/-/flot-axislabels-1.0.0.tgz", - "integrity": "sha1-NZKW0Il+PwAtoCqDdFIOhbYNNJw=" + "node_modules/cacache/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" + } }, - "flot-pie": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flot-pie/-/flot-pie-1.0.0.tgz", - "integrity": "sha1-HHUKUFNapkC6L1pbz3sy2N0g3sE=" + "node_modules/cacache/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 }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "engines": { + "node": ">=6" } }, - "follow-redirects": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.10.0.tgz", - "integrity": "sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==", + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", "dev": true, - "requires": { - "debug": "^3.0.0" - }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "no-case": "^2.2.0", + "upper-case": "^1.1.1" } }, - "font-atlas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz", - "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==", - "requires": { - "css-font": "^1.0.0" + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" } }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + "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" + } }, - "font-measure": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz", - "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==", - "requires": { - "css-font": "^1.2.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==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" + "node_modules/caniuse-lite": { + "version": "1.0.30001506", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz", + "integrity": "sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvas-fit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", + "integrity": "sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==", + "dependencies": { + "element-size": "^1.1.1" } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "node_modules/cdt2d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cdt2d/-/cdt2d-1.0.0.tgz", + "integrity": "sha512-pFKb7gVhpsI6onS5HUXRoqbBIJB4CJ+KPk8kgaIVcm0zFgOxIyBT5vzifZ4j1aoGVJS0U1A+S4oFDshuLAitlA==", + "dependencies": { + "binary-search-bounds": "^2.0.3", + "robust-in-sphere": "^1.1.3", + "robust-orientation": "^1.1.3" + } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "node_modules/cell-orientation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cell-orientation/-/cell-orientation-1.0.1.tgz", + "integrity": "sha512-DtEsrgP+donmPxpEZm7hK8zCPYDXAQ977ecJiE7G0gbTfnS6TZVBlief3IdRP/TZS1PVnJRGJTDdjSdV8mRDug==" }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true + "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" + } }, - "fraction.js": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.13.tgz", - "integrity": "sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA==", - "dev": true + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, - "requires": { - "map-cache": "^0.2.2" + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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" } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "node_modules/ci": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ci/-/ci-2.2.0.tgz", + "integrity": "sha512-lBkEN6XclyW0jnprtFQ+dsbP+9zwmo37Z1cV38h4FSDgI2QzFqwknJnVSvRxK9UXkPC4ZcVOVFyCVrNylTX52Q==", + "bin": { + "ci": "lib/ci.js" + }, + "funding": { + "url": "https://github.com/privatenumber/ci?sponsor=1" } }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" } }, - "fs.realpath": { + "node_modules/circumcenter": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", - "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", - "dev": true, + "resolved": "https://registry.npmjs.org/circumcenter/-/circumcenter-1.0.0.tgz", + "integrity": "sha512-YRw0mvttcISviaOtSmaHb2G3ZVbkxzYPQeAEd57/CFFtmOkwfRTw9XuxYZ7PCi2BYa0NajjHV6bq4nbY1VCC8g==", + "dependencies": { + "dup": "^1.0.0", + "robust-linear-solve": "^1.0.0" + } + }, + "node_modules/circumradius": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/circumradius/-/circumradius-1.0.0.tgz", + "integrity": "sha512-5ltoQvWQzJiZjCVX9PBKgKt+nsuzOLKayqXMNllfRSqIp2L5jFpdanv1V6j27Ue7ACxlzmamlR+jnLy+NTTVTw==", + "dependencies": { + "circumcenter": "^1.0.0" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true, + "peer": true + }, + "node_modules/clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==" + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-pslg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz", + "integrity": "sha512-bJnEUR6gRiiNi2n4WSC6yrc0Hhn/oQDOTzs6evZfPwEF/VKVXM6xu0F4n/WSBz7TjTt/ZK6I5snRM9gVKMVAxA==", + "dependencies": { + "big-rat": "^1.0.3", + "box-intersect": "^1.0.1", + "nextafter": "^1.0.0", + "rat-vec": "^1.1.1", + "robust-segment-intersect": "^1.0.1", + "union-find": "^1.0.2", + "uniq": "^1.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true, + "peer": true + }, + "node_modules/color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==", + "dev": true, + "dependencies": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "node_modules/color-alpha": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz", + "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==", + "dependencies": { + "color-parse": "^1.3.8" + } + }, + "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-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz", + "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==", + "dependencies": { + "clamp": "^1.0.1" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-normalize": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz", + "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==", + "dependencies": { + "clamp": "^1.0.1", + "color-rgba": "^2.1.1", + "dtype": "^2.0.0" + } + }, + "node_modules/color-parse": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz", + "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==", + "dependencies": { + "color-name": "^1.0.0", + "defined": "^1.0.0", + "is-plain-obj": "^1.1.0" + } + }, + "node_modules/color-rgba": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.1.1.tgz", + "integrity": "sha512-VaX97wsqrMwLSOR6H7rU1Doa2zyVdmShabKrPEIFywLlHoibgD3QW9Dw6fSqM4+H/LfjprDNAUUW31qEQcGzNw==", + "dependencies": { + "clamp": "^1.0.1", + "color-parse": "^1.3.8", + "color-space": "^1.14.6" + } + }, + "node_modules/color-space": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.16.0.tgz", + "integrity": "sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg==", + "dependencies": { + "hsluv": "^0.0.3", + "mumath": "^3.3.4" + } + }, + "node_modules/color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colormap": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.2.tgz", + "integrity": "sha512-jDOjaoEEmA9AgA11B/jCSAvYE95r3wRoAyTf3LEHGiUVlNHJaL1mRkf5AyLSpQBVGfTEPwGEqCIzL+kgr2WgNA==", + "dependencies": { + "lerp": "^1.0.3" + } + }, + "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==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compare-angle": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/compare-angle/-/compare-angle-1.0.1.tgz", + "integrity": "sha512-adM1/bpLFQFquh0/Qr5aiOPuztoga/lCf2Z45s+Oydgzf18F3wBSkdHmcHMeig0bD+dDKlz52u1rLOAOqiyE5A==", + "dependencies": { + "robust-orientation": "^1.0.2", + "robust-product": "^1.0.0", + "robust-sum": "^1.0.0", + "signum": "^0.0.0", + "two-sum": "^1.0.0" + } + }, + "node_modules/compare-angle/node_modules/signum": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz", + "integrity": "sha512-nct2ZUmwemVxeuPY5h+JLpHGJvLCXXNahGVI7IB3a6Fy5baX9AGSb854HceYH4FBw4eGjoZfEo9YRfkGfKdZQA==" + }, + "node_modules/compare-cell": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/compare-cell/-/compare-cell-1.0.0.tgz", + "integrity": "sha512-uNIkjiNLZLhdCgouF39J+W04R7oP1vwrNME4vP2b2/bAa6PHOj+h8yXu52uPjPTKs5RatvqNsDVwEN7Yp19vNA==" + }, + "node_modules/compare-oriented-cell": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz", + "integrity": "sha512-9D7R2MQfsGGRskZAZF0TkJHt9eFNbFkZyVdVps+WUYxtRHgG77BLbieKgSkj7iEAb9PNDSU9QNa9MtigjQ3ktQ==", + "dependencies": { + "cell-orientation": "^1.0.1", + "compare-cell": "^1.0.0" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/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==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "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/condense-newlines": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", + "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-whitespace": "^0.3.0", + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + }, + "node_modules/convex-hull": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz", + "integrity": "sha512-24rZAoh81t41GHPLAxcsokgjH9XNoVqU2OiSi8iMHUn6HUURfiefcEWAPt1AfwZjBBWTKadOm1xUcUMnfFukhQ==", + "dependencies": { + "affine-hull": "^1.0.0", + "incremental-convex-hull": "^1.0.1", + "monotone-convex-hull-2d": "^1.0.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-webpack-plugin": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz", + "integrity": "sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==", + "dev": true, + "dependencies": { + "cacache": "^15.0.5", + "fast-glob": "^3.2.4", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "webpack-sources": "^1.4.3" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.0.tgz", + "integrity": "sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "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/country-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", + "integrity": "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-color-function": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", + "integrity": "sha512-YD/WhiRZIYgadwFJ48X5QmlOQ/w8Me4yQI6/eSUoiE8spIFp+S/rGpsAH48iyq/0ZWkCDWqVQKUlQmUzn7BQ9w==", + "dev": true, + "dependencies": { + "balanced-match": "0.1.0", + "color": "^0.11.0", + "debug": "^3.1.0", + "rgb": "~0.1.0" + } + }, + "node_modules/css-color-function/node_modules/balanced-match": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.1.0.tgz", + "integrity": "sha512-4xb6XqAEo3Z+5pEDJz33R8BZXI8FRJU+cDNLdKgDpmnz+pKKRVYLpdv+VvUAC7yUhBMj4izmyt19eCGv1QGV7A==", + "dev": true + }, + "node_modules/css-color-function/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", + "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-font": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz", + "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==", + "dependencies": { + "css-font-size-keywords": "^1.0.0", + "css-font-stretch-keywords": "^1.0.1", + "css-font-style-keywords": "^1.0.1", + "css-font-weight-keywords": "^1.0.0", + "css-global-keywords": "^1.0.1", + "css-system-font-keywords": "^1.0.0", + "pick-by-alias": "^1.2.0", + "string-split-by": "^1.0.0", + "unquote": "^1.1.0" + } + }, + "node_modules/css-font-size-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz", + "integrity": "sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==" + }, + "node_modules/css-font-stretch-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz", + "integrity": "sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==" + }, + "node_modules/css-font-style-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz", + "integrity": "sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==" + }, + "node_modules/css-font-weight-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz", + "integrity": "sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==" + }, + "node_modules/css-global-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz", + "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ==" + }, + "node_modules/css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/css-loader/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/css-loader/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-loader/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/css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "dev": true, + "dependencies": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-system-font-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", + "integrity": "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==" + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.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": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, + "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": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/csv-file-validator": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/csv-file-validator/-/csv-file-validator-1.13.1.tgz", + "integrity": "sha512-2b1GRaTFlNN5pgnEBFpfDAfZK4mJ2Wvni8iJpkqte0wCUAHt1qrsZgchDODqM8gl/ZZ80TgPtO3VWXVLwHOyTw==", + "dependencies": { + "famulus": "^2.2.3", + "lodash": "^4.17.21", + "papaparse": "^5.3.1" + } + }, + "node_modules/cubic-hermite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz", + "integrity": "sha512-DKZ6yLcJiJJgl54mGA4n0uueYB4qdPfOJrQ1HSEZqdKp6D25AAAWVDwpoAxLflOku5a/ALBO77oEIyWcVa+UYg==" + }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "dependencies": { + "uniq": "^1.0.0" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/d3": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", + "integrity": "sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg==" + }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "node_modules/d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" + }, + "node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "node_modules/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "node_modules/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" + }, + "node_modules/d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "node_modules/d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "dependencies": { + "d3-color": "1 - 2" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + }, + "node_modules/d3-scale": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", + "dependencies": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz", + "integrity": "sha512-LLqy7dJSL8yDy7NRmf6xSlsFZ6zYvJ4BcWFE4zBrOPnQERv9zj24ohnXKRbyi9YHnYV+HN1oEO3iFK971/gkzA==", + "dependencies": { + "d3-color": "1 - 2", + "d3-interpolate": "1 - 2" + } + }, + "node_modules/d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==" + }, + "node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "dependencies": { + "d3-array": "2" + } + }, + "node_modules/d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "dependencies": { + "d3-time": "1 - 2" + } + }, + "node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "peerDependencies": { + "date-fns": ">=2.0.0" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "peer": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delaunay-triangulate": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/delaunay-triangulate/-/delaunay-triangulate-1.1.6.tgz", + "integrity": "sha512-mhAclqFCgLoiBIDQDIz2K+puZq6OhYxunXrG2wtTcZS+S1xuzl+H3h0MIOajpES+Z+jfY/rz0wVt3o5iipt1wg==", + "dependencies": { + "incremental-convex-hull": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-kerning": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz", + "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-event-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.1.0.tgz", + "integrity": "sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==", + "dev": true + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "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==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "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/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/double-bits": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz", + "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg==" + }, + "node_modules/draw-svg-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz", + "integrity": "sha512-P8j3IHxcgRMcY6sDzr0QvJDLzBnJJqpTG33UZ2Pvp8rw0apCHhJCWqYprqrXjrgHnJ6tuhP1iTJSAodPDHxwkg==", + "dependencies": { + "abs-svg-path": "~0.1.1", + "normalize-svg-path": "~0.1.0" + } + }, + "node_modules/dtype": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", + "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "node_modules/edges-to-adjacency-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz", + "integrity": "sha512-0n0Z+xTLfg96eYXm91PEY4rO4WGxohLWjJ9qD1RI3fzxKU6GHez+6KPajpobR4zeZxp7rSiHjHG5dZPj8Kj58Q==", + "dependencies": { + "uniq": "^1.0.0" + } + }, + "node_modules/editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "dependencies": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "bin": { + "editorconfig": "bin/editorconfig" + } + }, + "node_modules/editorconfig/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==", + "dev": true + }, + "node_modules/editorconfig/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/editorconfig/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/editorconfig/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.437", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.437.tgz", + "integrity": "sha512-ZFekRuBOHUXp21wrR5lshT6pZa/KmjkhKBAtmZz4NN5sCWlHOk3kdhiwFINrDBsRLX6FjyBAb1TRN+KBeNlyzQ==" + }, + "node_modules/element-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", + "integrity": "sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==" + }, + "node_modules/elementary-circuits-directed-graph": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.3.1.tgz", + "integrity": "sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==", + "dependencies": { + "strongly-connected-components": "^1.0.1" + } + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "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==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.9.0.tgz", + "integrity": "sha512-RODB4txU+xImYDemN5DqaKC0CHk05XSVkOX4pq0hK26Qx+1LChkuOyUDlGEjYb3ACr0n9qBhFjg37hQuJvpkRQ==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", + "dev": true + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha512-sziUVwcvQ+lOsrTyUY0Q11ilAPj+dy7AQ1E1MgSaHTaaAFTffaa08QSlGNU61iyVaroyb6nYdBV6oD7nzn6i8w==", + "dev": true, + "dependencies": { + "recast": "~0.11.12", + "through": "~2.3.6" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "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": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "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/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "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==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-4.0.2.tgz", + "integrity": "sha512-EDpXor6lsjtTzZpLUn7KmXs02+nIjGcgees9BYjNkWra3jVq5vVa8IoCKgzT2M7dNNeoMBtaSG83Bd40N3poLw==", + "deprecated": "This loader has been deprecated. Please use eslint-webpack-plugin", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "loader-utils": "^2.0.0", + "object-hash": "^2.0.3", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/eslint-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-plugin-backbone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-backbone/-/eslint-plugin-backbone-2.1.1.tgz", + "integrity": "sha512-z+vfSft/gjNQXuG9Gvyo0AYmlAXyS2e01MPKeHLTr2lSow3bOuFQDpJA3TwopgVAC2d3edMOH7aeNJIyTS4t3w==", + "dev": true, + "peerDependencies": { + "eslint": ">=2.0.0" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.15.0.tgz", + "integrity": "sha512-XYzpK6e2REli100+6iCeBA69v6Sm0D/yK2FZP+fCeNt0yH/m82qZQq+ztseyV0JsKdhFysuSEzeE1yCmSC92BA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.3.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^9.3.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-vue/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/eslint-plugin-vue/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-vue/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/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eslint/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/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==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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/eslint/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=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": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "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/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-frustum-planes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz", + "integrity": "sha512-GivvxEMgjSNnB3e1mIMBlB5ogPB6XyEjOQRGG0SfYVVLtu1ntLGHLT1ly8+mE819dKBHBwnm9+UBCScjiMgppA==" + }, + "node_modules/falafel": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz", + "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==", + "dependencies": { + "acorn": "^7.1.1", + "isarray": "^2.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/famulus": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/famulus/-/famulus-2.2.3.tgz", + "integrity": "sha512-tEh0NlWBtXSu1t/uY1eN7DQbXXcezPUp2/q25Scbc0h+Wivu9GHcdVnzlOqhD6hetpaj9CMhRm5InSQscM7FWQ==", + "dependencies": { + "lodash": "^4.17.20" + } + }, + "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==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "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.6.0" + } + }, + "node_modules/fast-isnumeric": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.4.tgz", + "integrity": "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw==", + "dependencies": { + "is-string-blank": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.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==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filtered-vector": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.5.tgz", + "integrity": "sha512-5Vu6wdtQJ1O2nRmz39dIr9m3hEDq1skYby5k1cJQdNWK4dMgvYcUEiA/9j7NcKfNZ5LGxn8w2LSLiigyH7pTAw==", + "dependencies": { + "binary-search-bounds": "^2.0.0", + "cubic-hermite": "^1.0.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/flatten-vertex-data": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", + "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==", + "dependencies": { + "dtype": "^2.0.0" + } + }, + "node_modules/flot-axislabels": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flot-axislabels/-/flot-axislabels-1.0.0.tgz", + "integrity": "sha512-Aiz5Za+nnZBjaTlhBcyBGc7uszgsNL7laSZ3jw++u8C4wn2QXg20sQlrUShARpNyvtdgdBDK7gIoIJbl8/HV3Q==" + }, + "node_modules/flot-pie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flot-pie/-/flot-pie-1.0.0.tgz", + "integrity": "sha512-wf/d6rYkyuSNS3AU7NANVY+tqEv5BkERwDB04zG8DsYeLukwR41Cr2DNmdz0Zrh5Q+Z34Q+atUH1iWKzy8Sv1A==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/font-atlas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz", + "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==", + "dependencies": { + "css-font": "^1.0.0" + } + }, + "node_modules/font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", + "engines": { + "node": ">=0.10.3" + } + }, + "node_modules/font-measure": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz", + "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==", + "dependencies": { + "css-font": "^1.2.0" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "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/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/gamma": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz", + "integrity": "sha512-IgHc/jnzNTA2KjXmRSx/CVd1ONp7HTAV81SLI+n3G6PyyHkakkE+2d3hteJYFm7aoe01NEl4m7ziUAsoWCc5AA==" + }, + "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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-canvas-context": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz", + "integrity": "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gl-axes3d": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/gl-axes3d/-/gl-axes3d-1.5.3.tgz", + "integrity": "sha512-KRYbguKQcDQ6PcB9g1pgqB8Ly4TY1DQODpPKiDTasyWJ8PxQk0t2Q7XoQQijNqvsguITCpVVCzNb5GVtIWiVlQ==", + "dependencies": { + "bit-twiddle": "^1.0.2", + "dup": "^1.0.0", + "extract-frustum-planes": "^1.0.0", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-state": "^1.0.0", + "gl-vao": "^1.3.0", + "gl-vec4": "^1.0.1", + "glslify": "^7.0.0", + "robust-orientation": "^1.1.3", + "split-polygon": "^1.0.0", + "vectorize-text": "^3.2.1" + } + }, + "node_modules/gl-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/gl-buffer/-/gl-buffer-2.1.2.tgz", + "integrity": "sha512-uVvLxxhEbQGl43xtDeKu75ApnrGyNHoPmOcvvuJNyP04HkK0/sX5Dll6OFffQiwSV4j0nlAZsgznvO3CPT3dFg==", + "dependencies": { + "ndarray": "^1.0.15", + "ndarray-ops": "^1.1.0", + "typedarray-pool": "^1.0.0" + } + }, + "node_modules/gl-cone3d": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.5.2.tgz", + "integrity": "sha512-1JNeHH4sUtUmDA4ZK7Om8/kShwb8IZVAsnxaaB7IPRJsNGciLj1sTpODrJGeMl41RNkex5kXD2SQFrzyEAR2Rw==", + "dependencies": { + "colormap": "^2.3.1", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", + "gl-vec3": "^1.1.3", + "glsl-inverse": "^1.0.0", + "glsl-out-of-range": "^1.0.4", + "glsl-specular-cook-torrance": "^2.0.1", + "glslify": "^7.0.0", + "ndarray": "^1.0.18" + } + }, + "node_modules/gl-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gl-constants/-/gl-constants-1.0.0.tgz", + "integrity": "sha512-3DNyoAUdb1c+o7jNk5Nm7eh6RSQFi9ZmMQIQb2xxsO27rUopE+IUhoh4xlUvZYBn1YPgUC8BlCnrVjXq/d2dQA==" + }, + "node_modules/gl-contour2d": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/gl-contour2d/-/gl-contour2d-1.1.7.tgz", + "integrity": "sha512-GdebvJ9DtT3pJDpoE+eU2q+Wo9S3MijPpPz5arZbhK85w2bARmpFpVfPaDlZqWkB644W3BlH8TVyvAo1KE4Bhw==", + "dependencies": { + "binary-search-bounds": "^2.0.4", + "cdt2d": "^1.0.0", + "clean-pslg": "^1.1.2", + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "glslify": "^7.0.0", + "iota-array": "^1.0.0", + "ndarray": "^1.0.18", + "surface-nets": "^1.0.2" + } + }, + "node_modules/gl-error3d": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/gl-error3d/-/gl-error3d-1.0.16.tgz", + "integrity": "sha512-TGJewnKSp7ZnqGgG3XCF9ldrDbxZrO+OWlx6oIet4OdOM//n8xJ5isArnIV/sdPJnFbhfoLxWrW9f5fxHFRQ1A==", + "dependencies": { + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "gl-vao": "^1.3.0", + "glsl-out-of-range": "^1.0.4", + "glslify": "^7.0.0" + } + }, + "node_modules/gl-fbo": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/gl-fbo/-/gl-fbo-2.0.5.tgz", + "integrity": "sha512-tDq6zQSQzvvK2QwPV7ln7cf3rs0jV1rQXqKOEuB145LdN+xhADPBtXHDJ3Ftk80RAJimJU0AaQBgP/X6yYGNhQ==", + "dependencies": { + "gl-texture2d": "^2.0.0" + } + }, + "node_modules/gl-format-compiler-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gl-format-compiler-error/-/gl-format-compiler-error-1.0.3.tgz", + "integrity": "sha512-FtQaBYlsM/rnz7YhLkxG9dLcNDB+ExErIsFV2DXl0nk+YgIZ2i0jMob4BrhT9dNa179zFb0gZMWpNAokytK+Ug==", + "dependencies": { + "add-line-numbers": "^1.0.1", + "gl-constants": "^1.0.0", + "glsl-shader-name": "^1.0.0", + "sprintf-js": "^1.0.3" + } + }, + "node_modules/gl-heatmap2d": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gl-heatmap2d/-/gl-heatmap2d-1.1.1.tgz", + "integrity": "sha512-6Vo1fPIB1vQFWBA/MR6JAA16XuQuhwvZRbSjYEq++m4QV33iqjGS2HcVIRfJGX+fomd5eiz6bwkVZcKm69zQPw==", + "dependencies": { + "binary-search-bounds": "^2.0.4", + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "glslify": "^7.0.0", + "iota-array": "^1.0.0", + "typedarray-pool": "^1.2.0" + } + }, + "node_modules/gl-line3d": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gl-line3d/-/gl-line3d-1.2.1.tgz", + "integrity": "sha512-eeb0+RI2ZBRqMYJK85SgsRiJK7c4aiOjcnirxv0830A3jmOc99snY3AbPcV8KvKmW0Yaf3KA4e+qNCbHiTOTnA==", + "dependencies": { + "binary-search-bounds": "^2.0.4", + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", + "glsl-out-of-range": "^1.0.4", + "glslify": "^7.0.0", + "ndarray": "^1.0.18" + } + }, + "node_modules/gl-mat3": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gl-mat3/-/gl-mat3-1.0.0.tgz", + "integrity": "sha512-obeEq9y7xaDoVkwMGJNL1upwpYlPJiXJFhREaNytMqUdfHKHNna9HvImmLV8F8Ys6QOYwPPddptZNoiiec/XOg==" + }, + "node_modules/gl-mat4": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz", + "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==" + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "node_modules/gl-mesh3d": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.3.1.tgz", + "integrity": "sha512-pXECamyGgu4/9HeAQSE5OEUuLBGS1aq9V4BCsTcxsND4fNLaajEkYKUz/WY2QSYElqKdsMBVsldGiKRKwlybqA==", + "dependencies": { + "barycentric": "^1.0.1", + "colormap": "^2.3.1", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", + "glsl-out-of-range": "^1.0.4", + "glsl-specular-cook-torrance": "^2.0.1", + "glslify": "^7.0.0", + "ndarray": "^1.0.18", + "normals": "^1.1.0", + "polytope-closest-point": "^1.0.0", + "simplicial-complex-contour": "^1.0.2", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-plot2d": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.4.5.tgz", + "integrity": "sha512-6GmCN10SWtV+qHFQ1gjdnVubeHFVsm6P4zmo0HrPIl9TcdePCUHDlBKWAuE6XtFhiMKMj7R8rApOX8O8uXUYog==", + "dependencies": { + "binary-search-bounds": "^2.0.4", + "gl-buffer": "^2.1.2", + "gl-select-static": "^2.0.7", + "gl-shader": "^4.2.1", + "glsl-inverse": "^1.0.0", + "glslify": "^7.0.0", + "text-cache": "^4.2.2" + } + }, + "node_modules/gl-plot3d": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.4.7.tgz", + "integrity": "sha512-mLDVWrl4Dj0O0druWyHUK5l7cBQrRIJRn2oROEgrRuOgbbrLAzsREKefwMO0bA0YqkiZMFMnV5VvPA9j57X5Xg==", + "dependencies": { + "3d-view": "^2.0.0", + "a-big-triangle": "^1.0.3", + "gl-axes3d": "^1.5.3", + "gl-fbo": "^2.0.5", + "gl-mat4": "^1.2.0", + "gl-select-static": "^2.0.7", + "gl-shader": "^4.2.1", + "gl-spikes3d": "^1.0.10", + "glslify": "^7.0.0", + "has-passive-events": "^1.0.0", + "is-mobile": "^2.2.1", + "mouse-change": "^1.4.0", + "mouse-event-offset": "^3.0.2", + "mouse-wheel": "^1.2.0", + "ndarray": "^1.0.19", + "right-now": "^1.0.0" + } + }, + "node_modules/gl-pointcloud2d": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gl-pointcloud2d/-/gl-pointcloud2d-1.0.3.tgz", + "integrity": "sha512-OS2e1irvJXVRpg/GziXj10xrFJm9kkRfFoB6BLUvkjCQV7ZRNNcs2CD+YSK1r0gvMwTg2T3lfLM3UPwNtz+4Xw==", + "dependencies": { + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "glslify": "^7.0.0", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-quat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gl-quat/-/gl-quat-1.0.0.tgz", + "integrity": "sha512-Pv9yvjJgQN85EbE79S+DF50ujxDkyjfYHIyXJcCRiimU1UxMY7vEHbVkj0IWLFaDndhfZT9vVOyfdMobLlrJsQ==", + "dependencies": { + "gl-mat3": "^1.0.0", + "gl-vec3": "^1.0.3", + "gl-vec4": "^1.0.0" + } + }, + "node_modules/gl-scatter3d": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.3.tgz", + "integrity": "sha512-nXqPlT1w5Qt51dTksj+DUqrZqwWAEWg0PocsKcoDnVNv0X8sGA+LBZ0Y+zrA+KNXUL0PPCX9WR9cF2uJAZl1Sw==", + "dependencies": { + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-vao": "^1.3.0", + "glsl-out-of-range": "^1.0.4", + "glslify": "^7.0.0", + "is-string-blank": "^1.0.1", + "typedarray-pool": "^1.1.0", + "vectorize-text": "^3.2.1" + } + }, + "node_modules/gl-select-box": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/gl-select-box/-/gl-select-box-1.0.4.tgz", + "integrity": "sha512-mKsCnglraSKyBbQiGq0Ila0WF+m6Tr+EWT2yfaMn/Sh9aMHq5Wt0F/l6Cf/Ed3CdERq5jHWAY5yxLviZteYu2w==", + "dependencies": { + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "glslify": "^7.0.0" + } + }, + "node_modules/gl-select-static": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/gl-select-static/-/gl-select-static-2.0.7.tgz", + "integrity": "sha512-OvpYprd+ngl3liEatBTdXhSyNBjwvjMSvV2rN0KHpTU+BTi4viEETXNZXFgGXY37qARs0L28ybk3UQEW6C5Nnw==", + "dependencies": { + "bit-twiddle": "^1.0.2", + "gl-fbo": "^2.0.5", + "ndarray": "^1.0.18", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-shader": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gl-shader/-/gl-shader-4.3.1.tgz", + "integrity": "sha512-xLoN6XtRLlg97SEqtuzfKc+pVWpVkQ3YjDI1kuCale8tF7+zMhiKlMfmG4IMQPMdKJZQbIc/Ny8ZusEpfh5U+w==", + "dependencies": { + "gl-format-compiler-error": "^1.0.2", + "weakmap-shim": "^1.1.0" + } + }, + "node_modules/gl-spikes2d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/gl-spikes2d/-/gl-spikes2d-1.0.2.tgz", + "integrity": "sha512-QVeOZsi9nQuJJl7NB3132CCv5KA10BWxAY2QgJNsKqbLsG53B/TrGJpjIAohnJftdZ4fT6b3ZojWgeaXk8bOOA==" + }, + "node_modules/gl-spikes3d": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/gl-spikes3d/-/gl-spikes3d-1.0.10.tgz", + "integrity": "sha512-lT3xroowOFxMvlhT5Mof76B2TE02l5zt/NIWljhczV2FFHgIVhA4jMrd5dIv1so1RXMBDJIKu0uJI3QKliDVLg==", + "dependencies": { + "gl-buffer": "^2.1.2", + "gl-shader": "^4.2.1", + "gl-vao": "^1.3.0", + "glslify": "^7.0.0" + } + }, + "node_modules/gl-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gl-state/-/gl-state-1.0.0.tgz", + "integrity": "sha512-Od836PpgCuTC0W7uHYnEEPRdQPL1FakWlznz3hRvlO6tD5sdLfBKX9qNRGy1DjfMCDTudhyYWxiWjhql1B8N4Q==", + "dependencies": { + "uniq": "^1.0.0" + } + }, + "node_modules/gl-streamtube3d": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.4.1.tgz", + "integrity": "sha512-rH02v00kgwgdpkXVo7KsSoPp38bIAYR9TE1iONjcQ4cQAlDhrGRauqT/P5sUaOIzs17A2DxWGcXM+EpNQs9pUA==", + "dependencies": { + "gl-cone3d": "^1.5.2", + "gl-vec3": "^1.1.3", + "gl-vec4": "^1.0.1", + "glsl-inverse": "^1.0.0", + "glsl-out-of-range": "^1.0.4", + "glsl-specular-cook-torrance": "^2.0.1", + "glslify": "^7.0.0" + } + }, + "node_modules/gl-surface3d": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.6.0.tgz", + "integrity": "sha512-x15+u4712ysnB85G55RLJEml6mOB4VaDn0VTlXCc9JcjRl5Es10Tk7lhGGyiPtkCfHwvhnkxzYA1/rHHYN7Y0A==", + "dependencies": { + "binary-search-bounds": "^2.0.4", + "bit-twiddle": "^1.0.2", + "colormap": "^2.3.1", + "dup": "^1.0.0", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", + "glsl-out-of-range": "^1.0.4", + "glsl-specular-beckmann": "^1.1.2", + "glslify": "^7.0.0", + "ndarray": "^1.0.18", + "ndarray-gradient": "^1.0.0", + "ndarray-ops": "^1.2.2", + "ndarray-pack": "^1.2.1", + "ndarray-scratch": "^1.2.0", + "surface-nets": "^1.0.2", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-text": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.3.1.tgz", + "integrity": "sha512-/f5gcEMiZd+UTBJLTl3D+CkCB/0UFGTx3nflH8ZmyWcLkZhsZ1+Xx5YYkw2rgWAzgPeE35xCqBuHSoMKQVsR+w==", + "dependencies": { + "bit-twiddle": "^1.0.2", + "color-normalize": "^1.5.0", + "css-font": "^1.2.0", + "detect-kerning": "^2.1.2", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "font-atlas": "^2.1.0", + "font-measure": "^1.2.2", + "gl-util": "^3.1.2", + "is-plain-obj": "^1.1.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "parse-unit": "^1.0.1", + "pick-by-alias": "^1.2.0", + "regl": "^2.0.0", + "to-px": "^1.0.1", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-text/node_modules/regl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/regl/-/regl-2.1.0.tgz", + "integrity": "sha512-oWUce/aVoEvW5l2V0LK7O5KJMzUSKeiOwFuJehzpSFd43dO5spP9r+sSUfhKtsky4u6MCqWJaRL+abzExynfTg==" + }, + "node_modules/gl-texture2d": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gl-texture2d/-/gl-texture2d-2.1.0.tgz", + "integrity": "sha512-W0tzEjtlGSsCKq5FFwFVhH+fONFUTUeqM4HhA/BleygKaX39IwNTVOiqkwfu9szQZ4dQEq8ZDl7w1ud/eKLaZA==", + "dependencies": { + "ndarray": "^1.0.15", + "ndarray-ops": "^1.2.2", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/gl-util": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.3.tgz", + "integrity": "sha512-dvRTggw5MSkJnCbh74jZzSoTOGnVYK+Bt+Ckqm39CVcl6+zSsxqWk4lr5NKhkqXHL6qvZAU9h17ZF8mIskY9mA==", + "dependencies": { + "is-browser": "^2.0.1", + "is-firefox": "^1.0.3", + "is-plain-obj": "^1.1.0", + "number-is-integer": "^1.0.1", + "object-assign": "^4.1.0", + "pick-by-alias": "^1.2.0", + "weak-map": "^1.0.5" + } + }, + "node_modules/gl-vao": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/gl-vao/-/gl-vao-1.3.0.tgz", + "integrity": "sha512-stSOZ+n0fnAxgDfipwKK/73AwzCNL+AFEc/v2Xm76nyFnUZGmQtD2FEC3lt1icoOHAzMgHBAjCue7dBIDeOTcw==" + }, + "node_modules/gl-vec3": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gl-vec3/-/gl-vec3-1.1.3.tgz", + "integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==" + }, + "node_modules/gl-vec4": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gl-vec4/-/gl-vec4-1.0.1.tgz", + "integrity": "sha512-/gx5zzIy75JXzke4yuwcbvK+COWf8UJbVCUPvhfsYVw1GVey4Eextk/0H0ctXnOICruNK7+GS4ILQzEQcHcPEg==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "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==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/glsl-inject-defines": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz", + "integrity": "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==", + "dependencies": { + "glsl-token-inject-block": "^1.0.0", + "glsl-token-string": "^1.0.1", + "glsl-tokenizer": "^2.0.2" + } + }, + "node_modules/glsl-inverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-inverse/-/glsl-inverse-1.0.0.tgz", + "integrity": "sha512-+BsseNlgqzd4IFX1dMqg+S0XuIXzH0acvTtW7svwhJESM1jb2BZFwdO+tOWdCXD5Zse6b9bOmzp5sCNA7GQ2QA==" + }, + "node_modules/glsl-out-of-range": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/glsl-out-of-range/-/glsl-out-of-range-1.0.4.tgz", + "integrity": "sha512-fCcDu2LCQ39VBvfe1FbhuazXEf0CqMZI9OYXrYlL6uUARG48CTAbL04+tZBtVM0zo1Ljx4OLu2AxNquq++lxWQ==" + }, + "node_modules/glsl-resolve": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz", + "integrity": "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA==", + "dependencies": { + "resolve": "^0.6.1", + "xtend": "^2.1.2" + } + }, + "node_modules/glsl-resolve/node_modules/resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==" + }, + "node_modules/glsl-resolve/node_modules/xtend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/glsl-shader-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-shader-name/-/glsl-shader-name-1.0.0.tgz", + "integrity": "sha512-OtHon0dPCbJD+IrVA1vw9QDlp2cS/f9z8X/0y+W7Qy1oZ3U1iFAQUEco2v30V0SAlVLDG5rEfhjEfc3DKdGbFQ==", + "dependencies": { + "atob-lite": "^1.0.0", + "glsl-tokenizer": "^2.0.2" + } + }, + "node_modules/glsl-specular-beckmann": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-specular-beckmann/-/glsl-specular-beckmann-1.1.2.tgz", + "integrity": "sha512-INvd7szO1twNPLGwE0Kf2xXIEy5wpOPl/LYoiw3+3nbAe6Rfn5rjdK9xvfnwoWksTCs3RejuLeAiZkLTkdFtwg==" + }, + "node_modules/glsl-specular-cook-torrance": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/glsl-specular-cook-torrance/-/glsl-specular-cook-torrance-2.0.1.tgz", + "integrity": "sha512-bFtTfbgLXIbto/U6gM7h0IxoPMU+5zpMK5HoAaA2LnPuGk3JSzKAnsoyh5QGTT8ioIEQrjk6jcQNrgujPsP7rw==", + "dependencies": { + "glsl-specular-beckmann": "^1.1.1" + } + }, + "node_modules/glsl-token-assignments": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz", + "integrity": "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ==" + }, + "node_modules/glsl-token-defines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz", + "integrity": "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ==", + "dependencies": { + "glsl-tokenizer": "^2.0.0" + } + }, + "node_modules/glsl-token-depth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz", + "integrity": "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg==" + }, + "node_modules/glsl-token-descope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz", + "integrity": "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw==", + "dependencies": { + "glsl-token-assignments": "^2.0.0", + "glsl-token-depth": "^1.1.0", + "glsl-token-properties": "^1.0.0", + "glsl-token-scope": "^1.1.0" + } + }, + "node_modules/glsl-token-inject-block": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz", + "integrity": "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA==" + }, + "node_modules/glsl-token-properties": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz", + "integrity": "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA==" + }, + "node_modules/glsl-token-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz", + "integrity": "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A==" + }, + "node_modules/glsl-token-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz", + "integrity": "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg==" + }, + "node_modules/glsl-token-whitespace-trim": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz", + "integrity": "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ==" + }, + "node_modules/glsl-tokenizer": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz", + "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", + "dependencies": { + "through2": "^0.6.3" + } + }, + "node_modules/glslify": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.1.1.tgz", + "integrity": "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog==", + "dependencies": { + "bl": "^2.2.1", + "concat-stream": "^1.5.2", + "duplexify": "^3.4.5", + "falafel": "^2.1.0", + "from2": "^2.3.0", + "glsl-resolve": "0.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glslify-bundle": "^5.0.0", + "glslify-deps": "^1.2.5", + "minimist": "^1.2.5", + "resolve": "^1.1.5", + "stack-trace": "0.0.9", + "static-eval": "^2.0.5", + "through2": "^2.0.1", + "xtend": "^4.0.0" + }, + "bin": { + "glslify": "bin.js" + } + }, + "node_modules/glslify-bundle": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz", + "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==", + "dependencies": { + "glsl-inject-defines": "^1.0.1", + "glsl-token-defines": "^1.0.0", + "glsl-token-depth": "^1.1.1", + "glsl-token-descope": "^1.0.2", + "glsl-token-scope": "^1.1.1", + "glsl-token-string": "^1.0.1", + "glsl-token-whitespace-trim": "^1.0.0", + "glsl-tokenizer": "^2.0.2", + "murmurhash-js": "^1.0.0", + "shallow-copy": "0.0.1" + } + }, + "node_modules/glslify-deps": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz", + "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==", + "dependencies": { + "@choojs/findup": "^0.2.0", + "events": "^3.2.0", + "glsl-resolve": "0.0.1", + "glsl-tokenizer": "^2.0.0", + "graceful-fs": "^4.1.2", + "inherits": "^2.0.1", + "map-limit": "0.0.1", + "resolve": "^1.0.0" + } + }, + "node_modules/glslify/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/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "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": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "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": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-hover": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-hover/-/has-hover-1.0.1.tgz", + "integrity": "sha512-0G6w7LnlcpyDzpeGUTuT0CEw05+QlMuGVk1IHNAlHrGJITGodjZu3x8BNDUMfKJSZXNB2ZAclqc1bvrd+uUpfg==", + "dependencies": { + "is-browser": "^2.0.1" + } + }, + "node_modules/has-passive-events": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-passive-events/-/has-passive-events-1.0.0.tgz", + "integrity": "sha512-2vSj6IeIsgvsRMyeQ0JaCX5Q3lX4zMn5HpoVc7MEhQ6pv8Iq9rsXjsp+E5ZwaT7T0xhMT0KmU8gtt1EFVdbJiw==", + "dependencies": { + "is-browser": "^2.0.1" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highcharts": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-7.2.2.tgz", + "integrity": "sha512-jRKujQuPKHLgGQd2sByBI9K5m56CInm2augVZnBYqdmyoU88hcI62uuAXHvxC3FW8YY0FJ74A1uw6sxrcmcmvg==" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hsluv": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-0.0.3.tgz", + "integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==" + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.6.tgz", + "integrity": "sha512-9o0+dcpIw2/HxkNuYKxSJUF/MMRZQECK4GnF+oQOmJ83yCVHTWgCH5aOXxK5bozNRmM8wtgryjHD3uloPBDEGw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "node_modules/html-loader": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz", + "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", + "dev": true, + "dependencies": { + "es6-templates": "^0.2.3", + "fastparse": "^1.1.1", + "html-minifier": "^3.5.8", + "loader-utils": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/html-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/html-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/html-minifier-terser/node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-minifier-terser/node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/html-minifier-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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "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/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", + "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imports-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.8.0.tgz", + "integrity": "sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ==", + "dev": true, + "dependencies": { + "loader-utils": "^1.0.2", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/imports-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/imports-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/imports-loader/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "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": "sha512-mKRJDXtzo1R9LxCuB1TdwZXHaPaIEldoGPsXy2jrJc/kufyqp8y/VAQQxThSxM2aroLoh6uObexPk1ASJ7FB7Q==", + "dependencies": { + "robust-orientation": "^1.1.2", + "simplicial-complex": "^1.0.0" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/interval-tree-1d": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz", + "integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==", + "dependencies": { + "binary-search-bounds": "^2.0.0" + } + }, + "node_modules/invert-permutation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-permutation/-/invert-permutation-1.0.0.tgz", + "integrity": "sha512-8f473/KSrnvyBd7Khr4PC5wPkAOehwkGc+AH5Q7D+U/fE+cdDob2FJ3naXAs4mspR9JIaEwbDI3me8H0KlVzSQ==" + }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, + "node_modules/ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-browser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", + "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==" + }, + "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.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "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": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "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==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-firefox": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-firefox/-/is-firefox-1.0.3.tgz", + "integrity": "sha512-6Q9ITjvWIm0Xdqv+5U12wgOKEM2KoBw4Y926m0OFkvlCxnbG94HKAsVz8w3fWcfAS5YA2fJORXX1dLrkprCCxA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-iexplorer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-iexplorer/-/is-iexplorer-1.0.0.tgz", + "integrity": "sha512-YeLzceuwg3K6O0MLM3UyUUjKAlyULetwryFp1mHy1I5PfArK0AEqlfa+MR4gkJjcbuJXoDJCvXbyqZVf5CR2Sg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-mobile": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.2.tgz", + "integrity": "sha512-wW/SXnYJkTjs++tVK5b6kVITZpAZPtUrt9SF80vvxGiF/Oywal+COk1jlRkiVq15RFNEQKQY31TkV24/1T5cVg==" + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string-blank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", + "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" + }, + "node_modules/is-svg-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-svg-path/-/is-svg-path-1.0.2.tgz", + "integrity": "sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg==" + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "peer": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "peer": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, + "peer": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-circus/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-circus/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-cli/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-cli/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-config/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-config/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-diff/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-diff/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-each/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-each/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz", + "integrity": "sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/jest-environment-jsdom/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/jest-environment-jsdom/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-environment-jsdom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-environment-jsdom/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-environment-jsdom/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==", + "dev": true + }, + "node_modules/jest-environment-jsdom/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-environment-jsdom/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-haste-map/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-haste-map/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-matcher-utils/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-message-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-message-util/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, + "peer": true, + "dependencies": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-resolve/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-resolve/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-runner/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-runner/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-runtime/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-runtime/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-snapshot/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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, + "peer": true + }, + "node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-util/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-validate/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-validate/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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/jest-watcher/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "peer": true + }, + "node_modules/jest-watcher/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/jest-worker/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-worker/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-worker/node_modules/chalk/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-worker/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==", + "dev": true + }, + "node_modules/jest-worker/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jquery": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz", + "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg==" + }, + "node_modules/jquery-color": { + "version": "3.0.0-alpha.2", + "resolved": "https://registry.npmjs.org/jquery-color/-/jquery-color-3.0.0-alpha.2.tgz", + "integrity": "sha512-dG5Y4YO/SQ7tgMYQjEu8zqFqx6gzrZyXhObGRIJkprBCaMsuH6vpxjXclK4d3qwkp1XZBlyOuf5Hv5Spjoy2aw==", + "peerDependencies": { + "jquery": ">=1.11.0" + } + }, + "node_modules/jquery-flot-resize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jquery-flot-resize/-/jquery-flot-resize-1.0.0.tgz", + "integrity": "sha512-754cos9Lt6Ujx/MYgA0hDRvhmFzHfjhBj4Avjb7fRUfFA/1XfpK8Stc0qNMiGWfv/HmWieRDKP6STCqefDm94A==" + }, + "node_modules/jquery-touchswipe": { + "version": "1.6.19", + "resolved": "https://registry.npmjs.org/jquery-touchswipe/-/jquery-touchswipe-1.6.19.tgz", + "integrity": "sha512-b0BGje9reNRU3u6ksAK9QqnX7yBRgLNe/wYG7DOfyDlhBlYjayIT8bSOHmcuvptIDW/ubM9CTW/mnZf9Rohuow==" + }, + "node_modules/jquery-ui": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.2.tgz", + "integrity": "sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==", + "dependencies": { + "jquery": ">=1.8.0 <4.0.0" + } + }, + "node_modules/jquery-ui-timepicker-addon": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/jquery-ui-timepicker-addon/-/jquery-ui-timepicker-addon-1.6.3.tgz", + "integrity": "sha512-JvZTp1vGN1Ws9tr39VPRBC1/cOVdY/pa6a2mMcAXpEgjrsXc02xBXQthDtQsBXEqR0FbZV+GdXi/c+LDpuyk/A==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/jquery.cookie": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", + "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" + }, + "node_modules/jquery.flot": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/jquery.flot/-/jquery.flot-0.8.3.tgz", + "integrity": "sha512-/tEE8J5NjwvStHDaCHkvTJpD7wDS4hE1OEL8xEmhgQfUe0gLUem923PIceNez1mz4yBNx6Hjv7pJcowLNd+nbg==" + }, + "node_modules/jquery.flot.tooltip": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/jquery.flot.tooltip/-/jquery.flot.tooltip-0.9.0.tgz", + "integrity": "sha512-TV3umIfIPRq1UM0EUY4E/jimrh2j057J6SJB1HQnEeS64r8liec++F0WCNYsmcWDxz6Lo4E2lCA+SeOQS2W3bA==" + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-beautify": { + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.8.tgz", + "integrity": "sha512-4S7HFeI9YfRvRgKnEweohs0tgJj28InHVIj4Nl8Htf96Y6pHg3+tJrmo4ucAM9f7l4SHbFI3IvFAZ2a1eQPbyg==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^0.15.3", + "glob": "^8.1.0", + "nopt": "^6.0.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/lerp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lerp/-/lerp-1.0.3.tgz", + "integrity": "sha512-70Rh4rCkJDvwWiTsyZ1HmJGvnyfFah4m6iTux29XmasRiZPDBpT9Cfa4ai73+uLZxnlKruUS62jj2lb11wURiA==" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "1.28.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", + "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", + "engines": { + "node": "*" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-limit": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", + "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==", + "dependencies": { + "once": "~1.3.0" + } + }, + "node_modules/map-limit/node_modules/once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/mapbox-gl": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.10.1.tgz", + "integrity": "sha512-0aHt+lFUpYfvh0kMIqXqNXqoYMuhuAsMlw87TbhWrw78Tx2zfuPI0Lx31/YPUgJ+Ire0tzQ4JnuBL7acDNXmMg==", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.0", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.5.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.1", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.2.1", + "grid-index": "^1.1.0", + "minimist": "^1.2.5", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^7.0.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/marching-simplex-table": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/marching-simplex-table/-/marching-simplex-table-1.0.0.tgz", + "integrity": "sha512-PexXXVF4f5Bux3vGCNlRRBqF/GyTerNo77PbBz8g/MFFXv212b48IGVglj/VfaYBRY6vlFQffa9dFbCCN0+7LA==", + "dependencies": { + "convex-hull": "^1.0.3" + } + }, + "node_modules/markdown": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz", + "integrity": "sha512-ctGPIcuqsYoJ493sCtFK7H4UEgMWAUdXeBhPbdsg1W0LsV9yJELAHRsMmWfTgao6nH0/x5gf9FmsbxiXnrgaIQ==", + "dependencies": { + "nopt": "~2.1.1" + }, + "bin": { + "md2html": "bin/md2html.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mat4-decompose": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mat4-decompose/-/mat4-decompose-1.0.4.tgz", + "integrity": "sha512-M3x6GXrzRTt5Ok4/bcHFc869Pe8F3uWaSp3xkUpi+uaTRulPXIZ1GWD13Z3A8WK2bxTrcvX21mjp05gUy/Dwbw==", + "dependencies": { + "gl-mat4": "^1.0.1", + "gl-vec3": "^1.0.2" + } + }, + "node_modules/mat4-interpolate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mat4-interpolate/-/mat4-interpolate-1.0.4.tgz", + "integrity": "sha512-+ulnoc6GUHq8eGZGbLyhQU61tx2oeNAFilV/xzCCzLV+F3nDk8jqERUqRmx8eNMMMvrdvoRSw0JXmnisfVPY9A==", + "dependencies": { + "gl-mat4": "^1.0.1", + "gl-vec3": "^1.0.2", + "mat4-decompose": "^1.0.3", + "mat4-recompose": "^1.0.3", + "quat-slerp": "^1.0.0" + } + }, + "node_modules/mat4-recompose": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mat4-recompose/-/mat4-recompose-1.0.4.tgz", + "integrity": "sha512-s1P2Yl4LQxq8dN0CgJE+mCO8y3IX/SmauSZ+H0zJsE1UKlgJ9loInfPC/OUxn2MzUW9bfBZf0Wcc2QKA3/e6FQ==", + "dependencies": { + "gl-mat4": "^1.0.1" + } + }, + "node_modules/math-log2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", + "integrity": "sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matrix-camera-controller": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.4.tgz", + "integrity": "sha512-zsPGPONclrKSImNpqqKDTcqFpWLAIwMXEJtCde4IFPOw1dA9udzFg4HOFytOTosOFanchrx7+Hqq6glLATIxBA==", + "dependencies": { + "binary-search-bounds": "^2.0.0", + "gl-mat4": "^1.1.2", + "gl-vec3": "^1.0.3", + "mat4-interpolate": "^1.0.3" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-source-map/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/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/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/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/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/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": "sha512-ixQ3qdXTVHvR7eAoOjKY8kGxl9YjOFtzi7qOjwmFFPfBqZHVOjUFOBy/Dk9dusamRSPJe9ggyfSypRbs0Bl8BA==", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/mouse-change": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz", + "integrity": "sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ==", + "dependencies": { + "mouse-event": "^1.0.0" + } + }, + "node_modules/mouse-event": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz", + "integrity": "sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw==" + }, + "node_modules/mouse-event-offset": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mouse-event-offset/-/mouse-event-offset-3.0.2.tgz", + "integrity": "sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w==" + }, + "node_modules/mouse-wheel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz", + "integrity": "sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw==", + "dependencies": { + "right-now": "^1.0.0", + "signum": "^1.0.0", + "to-px": "^1.0.1" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mumath": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/mumath/-/mumath-3.3.4.tgz", + "integrity": "sha512-VAFIOG6rsxoc7q/IaY3jdjmrsuX9f15KlRLYTHmixASBZkZEKC1IFqE2BC5CdhXmK6WLM1Re33z//AGmeRI6FA==", + "deprecated": "Redundant dependency in your project.", + "dependencies": { + "almost-equal": "^1.1.0" + } + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-extract-contour": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ndarray-extract-contour/-/ndarray-extract-contour-1.0.1.tgz", + "integrity": "sha512-iDngNoFRqrqbXGLP8BzyGrybw/Jnkkn7jphzc3ZFfO7dfmpL1Ph74/6xCi3xSvJFyVW90XpMnd766jTaRPsTCg==", + "dependencies": { + "typedarray-pool": "^1.0.0" + } + }, + "node_modules/ndarray-gradient": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ndarray-gradient/-/ndarray-gradient-1.0.1.tgz", + "integrity": "sha512-+xONVi7xxTCGL6KOb11Yyoe0tPNqAUKF39CvFoRjL5pdOmPd2G2pckK9lD5bpLF3q45LLnYNyiUSJSdNmQ2MTg==", + "dependencies": { + "cwise-compiler": "^1.0.0", + "dup": "^1.0.0" + } + }, + "node_modules/ndarray-linear-interpolate": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ndarray-linear-interpolate/-/ndarray-linear-interpolate-1.0.0.tgz", + "integrity": "sha512-UN0f4+6XWsQzJ2pP5gVp+kKn5tJed6mA3K/L50uO619+7LKrjcSNdcerhpqxYaSkbxNJuEN76N05yBBJySnZDw==" + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pack": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", + "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", + "dependencies": { + "cwise-compiler": "^1.1.2", + "ndarray": "^1.0.13" + } + }, + "node_modules/ndarray-scratch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ndarray-scratch/-/ndarray-scratch-1.2.0.tgz", + "integrity": "sha512-a4pASwB1jQyJcKLYrwrladVfDZDUGc78qLJZbHyb1Q4rhte0URhzc6ALQpBcauwgov0sXLwZz3vYH5jKAhSMIg==", + "dependencies": { + "ndarray": "^1.0.14", + "ndarray-ops": "^1.2.1", + "typedarray-pool": "^1.0.2" + } + }, + "node_modules/ndarray-sort": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ndarray-sort/-/ndarray-sort-1.0.1.tgz", + "integrity": "sha512-Gpyis5NvEPOQVadDOG+Dx8bhYCkaxn5IlA4Ig/jBJIlnW1caDiPneQLzT/+AIMeHEmqlGZfdqO/I1TXJS2neAw==", + "dependencies": { + "typedarray-pool": "^1.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/nextafter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz", + "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==", + "dependencies": { + "double-bits": "^1.1.0" + } + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "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": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + }, + "node_modules/nopt": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", + "integrity": "sha512-x8vXm7BZ2jE1Txrxh/hO74HTuYZQEbo8edoRcANgdZ4+PCV+pbjd/xdummkmjjC7LU5EjPzlu8zEq/oxWylnKA==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "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==", + "dev": true, + "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": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-svg-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", + "integrity": "sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA==" + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "node_modules/normals": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz", + "integrity": "sha512-XWeliW48BLvbVJ+cjQAOE+tA0m1M7Yi1iTPphAS9tBmW1A/c/cOVnEUecPCCMH5lEAihAcG6IRle56ls9k3xug==" + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==" + }, + "node_modules/number-is-integer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-integer/-/number-is-integer-1.0.1.tgz", + "integrity": "sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg==", + "dependencies": { + "is-finite": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/numeric": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz", + "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw==" + }, + "node_modules/nwsapi": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "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.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/orbit-camera-controller": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/orbit-camera-controller/-/orbit-camera-controller-4.0.0.tgz", + "integrity": "sha512-/XTmpr6FUT6MuKPBGN2nv9cS8jhhVs8do71VagBQS5p4rxM04MhqSnI/Uu+gVNN5s6KPcS73o1dHzjuDThEJUA==", + "dependencies": { + "filtered-vector": "^1.2.1", + "gl-mat4": "^1.0.3" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pad-left": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz", + "integrity": "sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==", + "dependencies": { + "repeat-string": "^1.3.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "dependencies": { + "no-case": "^2.2.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==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parenthesis": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz", + "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==" + }, + "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==", + "dev": true, + "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/parse-rect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", + "integrity": "sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==", + "dependencies": { + "pick-by-alias": "^1.2.0" + } + }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==" + }, + "node_modules/parse-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", + "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==" + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/permutation-parity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/permutation-parity/-/permutation-parity-1.0.0.tgz", + "integrity": "sha512-mRaEvnnWolbZuErWD08StRUZP9YOWG3cURP5nYpRg1D2PENzPXCUrPv8/bOk0tfln0hISLZjOdOcQCbsVpL2nQ==", + "dependencies": { + "typedarray-pool": "^1.0.0" + } + }, + "node_modules/permutation-rank": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz", + "integrity": "sha512-kmXwlQcd4JlV8g61jz0xDyroFNlJ/mP+KbSBllMuQD7FvaQInRnnAStElcppkUXd8qVFLvemy6msUmBn7sDzHg==", + "dependencies": { + "invert-permutation": "^1.0.0", + "typedarray-pool": "^1.0.0" + } + }, + "node_modules/pick-by-alias": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", + "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/planar-dual": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/planar-dual/-/planar-dual-1.0.2.tgz", + "integrity": "sha512-jfQCbX1kXu53+enC+BPQlfoZI1u5m8IUhFVtFG+9tUj84wnuaYNheR69avYWCNXWnUCkwUajmYMqX9M2Ruh4ug==", + "dependencies": { + "compare-angle": "^1.0.0", + "dup": "^1.0.0" + } + }, + "node_modules/planar-graph-to-polyline": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/planar-graph-to-polyline/-/planar-graph-to-polyline-1.0.6.tgz", + "integrity": "sha512-h8a9kdAjo7mRhC0X6HZ42xzFp7vKDZA+Hygyhsq/08Qi4vVAQYJaLLYLvKUUzRbVKvdYqq0reXHyV0EygyEBHA==", + "dependencies": { + "edges-to-adjacency-list": "^1.0.0", + "planar-dual": "^1.0.0", + "point-in-big-polygon": "^2.0.1", + "robust-orientation": "^1.0.1", + "robust-sum": "^1.0.0", + "two-product": "^1.0.0", + "uniq": "^1.0.0" + } + }, + "node_modules/plotly.js": { + "version": "1.58.5", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.58.5.tgz", + "integrity": "sha512-ChTlnFXB4tB0CzcG1mqgUKYnrJsZ8REDGox8BHAa/ltsd48MOAhOmFgjyDxwsXyjjgwOI296GeYDft8g4ftLHQ==", + "dependencies": { + "@plotly/d3-sankey": "0.7.2", + "@plotly/d3-sankey-circular": "0.33.1", + "@plotly/point-cluster": "^3.1.9", + "@turf/area": "^6.0.1", + "@turf/bbox": "^6.0.1", + "@turf/centroid": "^6.0.2", + "alpha-shape": "^1.0.0", + "canvas-fit": "^1.5.0", + "color-alpha": "1.0.4", + "color-normalize": "1.5.0", + "color-parse": "1.3.8", + "color-rgba": "2.1.1", + "convex-hull": "^1.0.3", + "country-regex": "^1.1.0", + "d3": "^3.5.17", + "d3-force": "^1.2.1", + "d3-hierarchy": "^1.1.9", + "d3-interpolate": "^1.4.0", + "d3-time-format": "^2.2.3", + "delaunay-triangulate": "^1.1.6", + "es6-promise": "^4.2.8", + "fast-isnumeric": "^1.1.4", + "gl-cone3d": "^1.5.2", + "gl-contour2d": "^1.1.7", + "gl-error3d": "^1.0.16", + "gl-heatmap2d": "^1.1.0", + "gl-line3d": "1.2.1", + "gl-mat4": "^1.2.0", + "gl-mesh3d": "^2.3.1", + "gl-plot2d": "^1.4.5", + "gl-plot3d": "^2.4.7", + "gl-pointcloud2d": "^1.0.3", + "gl-scatter3d": "^1.2.3", + "gl-select-box": "^1.0.4", + "gl-spikes2d": "^1.0.2", + "gl-streamtube3d": "^1.4.1", + "gl-surface3d": "^1.6.0", + "gl-text": "^1.1.8", + "glslify": "^7.1.1", + "has-hover": "^1.0.1", + "has-passive-events": "^1.0.0", + "image-size": "^0.7.5", + "is-mobile": "^2.2.2", + "mapbox-gl": "1.10.1", + "matrix-camera-controller": "^2.1.3", + "mouse-change": "^1.4.0", + "mouse-event-offset": "^3.0.2", + "mouse-wheel": "^1.2.0", + "ndarray": "^1.0.19", + "ndarray-linear-interpolate": "^1.0.0", + "parse-svg-path": "^0.1.2", + "polybooljs": "^1.2.0", + "regl": "^1.6.1", + "regl-error2d": "^2.0.11", + "regl-line2d": "^3.0.18", + "regl-scatter2d": "^3.2.1", + "regl-splom": "^1.0.12", + "right-now": "^1.0.0", + "robust-orientation": "^1.1.3", + "sane-topojson": "^4.0.0", + "strongly-connected-components": "^1.0.1", + "superscript-text": "^1.0.0", + "svg-path-sdf": "^1.1.3", + "tinycolor2": "^1.4.2", + "to-px": "1.0.1", + "topojson-client": "^3.1.0", + "webgl-context": "^2.2.0", + "world-calendars": "^1.0.3" + } + }, + "node_modules/plotly.js/node_modules/d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "node_modules/plotly.js/node_modules/d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/plotly.js/node_modules/d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "node_modules/plotly.js/node_modules/d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/point-in-big-polygon": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/point-in-big-polygon/-/point-in-big-polygon-2.0.1.tgz", + "integrity": "sha512-DtrN8pa2VfMlvmWlCcypTFeBE4+OYz1ojDNJLKCWa4doiVAD6PRBbxFYAT71tsp5oKaRXT5sxEiHCAQKb1zr2Q==", + "dependencies": { + "binary-search-bounds": "^2.0.0", + "interval-tree-1d": "^1.0.1", + "robust-orientation": "^1.1.3", + "slab-decomposition": "^1.0.1" + } + }, + "node_modules/polybooljs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/polybooljs/-/polybooljs-1.2.0.tgz", + "integrity": "sha512-mKjR5nolISvF+q2BtC1fi/llpxBPTQ3wLWN8+ldzdw2Hocpc8C72ZqnamCM4Z6z+68GVVjkeM01WJegQmZ8MEQ==" + }, + "node_modules/polytope-closest-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/polytope-closest-point/-/polytope-closest-point-1.0.0.tgz", + "integrity": "sha512-rvmt1e2ci9AUyWeHg+jsNuhGC4eBtxX4WjD9uDdvQzv2I1CVJSgbblJTslNXpGUu4KZSsUtSzvIdHKRKfRF3kw==", + "dependencies": { + "numeric": "^1.2.6" + } + }, + "node_modules/portal-vue": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.7.tgz", + "integrity": "sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==", + "peerDependencies": { + "vue": "^2.5.18" + } + }, + "node_modules/postcss": { + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-color-function": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-color-function/-/postcss-color-function-4.1.0.tgz", + "integrity": "sha512-2/fuv6mP5Lt03XbRpVfMdGC8lRP1sykme+H1bR4ARyOmSMB8LPSjcL6EAI1iX6dqUF+jNEvKIVVXhan1w/oFDQ==", + "dev": true, + "dependencies": { + "css-color-function": "~1.3.3", + "postcss": "^6.0.23", + "postcss-message-helpers": "^2.0.0", + "postcss-value-parser": "^3.3.1" + } + }, + "node_modules/postcss-color-function/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-color-function/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==", + "dev": true + }, + "node_modules/postcss-color-function/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-extend-rule": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-extend-rule/-/postcss-extend-rule-3.0.0.tgz", + "integrity": "sha512-gMlRmW52Y86Lct+KTr+gMjxk0aUSuTyjtvelIH3a7UdaXONaKH6qSmj8KAXCW4+nhTlAsOw5JdkRjCdZ/Kjb3Q==", + "dev": true, + "dependencies": { + "postcss": "^7.0.17", + "postcss-nesting": "^7.0.1", + "postcss-tape": "^5.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-extend-rule/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-extend-rule/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-extend-rule/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha512-N5yWXWKA+uhpLQ9ZhBRl2bIAdM6oVJYpDojuI1nF2SzXBimJcdjFwiAouBVbO5VuOF3qA6BSFWFc3wXbbj72XQ==", + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/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-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-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-import": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-13.0.0.tgz", + "integrity": "sha512-LPUbm3ytpYopwQQjqgUH4S3EM/Gb9QsaSPP/5vnoi+oKVy3/mIk2sc0Paqw7RL57GpScm9MdIMUypw2znWiBpg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/postcss-loader/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/postcss-loader/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-loader/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/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==", + "dev": true + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-mixins": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-7.0.3.tgz", + "integrity": "sha512-YLiJbOBiFmj3dX0gfo74fPDKtRvcQSntzgyqwD1noW1dne6sAJkuCtoOlGaFX8dLxcv9+qkOA6Uh1Ae0/6C56w==", + "dev": true, + "dependencies": { + "globby": "^11.0.2", + "postcss-js": "^3.0.3", + "postcss-simple-vars": "^6.0.3", + "sugarss": "^3.0.3" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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==", + "dev": true, + "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/postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-nesting/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-nesting/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-scss": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-3.0.5.tgz", + "integrity": "sha512-3e0qYk87eczfzg5P73ZVuuxEGCBfatRhPze6KrSaIbEKVtmnFI1RYp1Fv+AyZi+w8kcNRSPeNX6ap4b65zEkiA==", + "dev": true, + "dependencies": { + "postcss": "^8.2.7" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-simple-vars": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-6.0.3.tgz", + "integrity": "sha512-fkNn4Zio8vN4vIig9IFdb8lVlxWnYR769RgvxCM6YWlFKie/nQaOcaMMMFz/s4gsfHW4/5bJW+i57zD67mQU7g==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, + "node_modules/postcss-strip-inline-comments": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/postcss-strip-inline-comments/-/postcss-strip-inline-comments-0.1.5.tgz", + "integrity": "sha512-4EW5hYyv2syFyIBahkXGhZppp9zb5wD5NJ2R65WjXnB5q8T0g4VyLBTevU6ZpxtaN4HkoYZhV03DGUf5Ptd4FA==", + "dev": true, + "dependencies": { + "postcss": "^5.0.18" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "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/postcss-strip-inline-comments/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": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-strip-inline-comments/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-tape": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-tape/-/postcss-tape-5.0.2.tgz", + "integrity": "sha512-e4770WnsUzczQp/pAIsz0s0MDLAQ7luyh1/hs8QBcdfXOMrz0siEqYNHAKJIoCvGtLoi2QUjWASvTbPfyTfIWg==", + "dev": true, + "bin": { + "postcss-tape": "index.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-utilities": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/postcss-utilities/-/postcss-utilities-0.8.4.tgz", + "integrity": "sha512-Ut4cEjuCMvxd7sA+QWNppLjKcfFUbIT41NsNBfitE/2QX1eMZs5G4HskNxW2L4JE6HhWad91aWVWKKYTZVWx6g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.17", + "postcss-value-parser": "^4.0.0" + } + }, + "node_modules/postcss-utilities/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-utilities/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-utilities/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", + "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==", + "dev": true, + "dependencies": { + "condense-newlines": "^0.2.1", + "extend-shallow": "^2.0.1", + "js-beautify": "^1.6.12" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "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/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/purgecss/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/purgecss/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/purgecss/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/purgecss/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/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quat-slerp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/quat-slerp/-/quat-slerp-1.0.1.tgz", + "integrity": "sha512-OTozCDeP5sW7cloGR+aIycctZasBhblk1xdsSGP1Iz5pEwDqyChloTmc96xsDfusFD7GRxwDDu+tpJX0Wa1kJw==", + "dependencies": { + "gl-quat": "^1.0.0" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "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==", + "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/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "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==", + "dev": true, + "dependencies": { + "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==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rat-vec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz", + "integrity": "sha512-FbxGwkQxmw4Jx41LR7yMOR+g8M9TWCEmf/SUBQVLuK2eh0nThnffF7IUualr3XE2x5F8AdLiCVeSGwXd4snfgg==", + "dependencies": { + "big-rat": "^1.0.3" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "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==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha512-+nixG+3NugceyR8O1bLU45qs84JgI3+8EauyRZafLgC9XbdAOIVgwV1Pe2da0YzGo62KzWoZwUpVEQf6qNAXWA==", + "dev": true, + "dependencies": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/recast/node_modules/esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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/reduce-simplicial-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-simplicial-complex/-/reduce-simplicial-complex-1.0.0.tgz", + "integrity": "sha512-t+nT7sHDtcxBx8TbglqfLsLKoFiSn9hp6GFojJEThHBAFv72wQeq/uRiPYZa4Xb8FR1Ye1foRcBV3Ki6bgm+pQ==", + "dependencies": { + "cell-orientation": "^1.0.1", + "compare-cell": "^1.0.0", + "compare-oriented-cell": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "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": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/regl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/regl/-/regl-1.7.0.tgz", + "integrity": "sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w==" + }, + "node_modules/regl-error2d": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.12.tgz", + "integrity": "sha512-r7BUprZoPO9AbyqM5qlJesrSRkl+hZnVKWKsVp7YhOl/3RIpi4UDGASGJY0puQ96u5fBYw/OlqV24IGcgJ0McA==", + "dependencies": { + "array-bounds": "^1.0.1", + "color-normalize": "^1.5.0", + "flatten-vertex-data": "^1.0.2", + "object-assign": "^4.1.1", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-line2d": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.2.tgz", + "integrity": "sha512-nmT7WWS/WxmXAQMkgaMKWXaVmwJ65KCrjbqHGOUjjqQi6shfT96YbBOvelXwO9hG7/hjvbzjtQ2UO0L3e7YaXQ==", + "dependencies": { + "array-bounds": "^1.0.1", + "array-find-index": "^1.0.2", + "array-normalize": "^1.1.4", + "color-normalize": "^1.5.0", + "earcut": "^2.1.5", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", + "glslify": "^7.0.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0" + } + }, + "node_modules/regl-scatter2d": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.2.9.tgz", + "integrity": "sha512-PNrXs+xaCClKpiB2b3HZ2j3qXQXhC5kcTh/Nfgx9rLO0EpEhab0BSQDqAsbdbpdf+pSHSJvbgitB7ulbGeQ+Fg==", + "dependencies": { + "@plotly/point-cluster": "^3.1.9", + "array-range": "^1.0.1", + "array-rearrange": "^2.2.2", + "clamp": "^1.0.1", + "color-id": "^1.1.0", + "color-normalize": "^1.5.0", + "color-rgba": "^2.1.1", + "flatten-vertex-data": "^1.0.2", + "glslify": "^7.0.0", + "is-iexplorer": "^1.0.0", + "object-assign": "^4.1.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "to-float32": "^1.1.0", + "update-diff": "^1.1.0" + } + }, + "node_modules/regl-splom": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.14.tgz", + "integrity": "sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw==", + "dependencies": { + "array-bounds": "^1.0.1", + "array-range": "^1.0.1", + "color-alpha": "^1.0.4", + "flatten-vertex-data": "^1.0.2", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0", + "raf": "^3.4.1", + "regl-scatter2d": "^3.2.3" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgb": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rgb/-/rgb-0.1.0.tgz", + "integrity": "sha512-F49dXX73a92N09uQkfCp2QjwXpmJcn9/i9PvjmwsSIXUGqRLCf/yx5Q9gRxuLQTq248kakqQuc8GX/U/CxSqlA==", + "dev": true, + "bin": { + "rgb": "bin/rgb" + } + }, + "node_modules/right-now": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz", + "integrity": "sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-compress": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-compress/-/robust-compress-1.0.0.tgz", + "integrity": "sha512-E8btSpQ6zZr7LvRLrLvb+N5rwQ0etUbsXFKv5NQj6TVK6RYT00Qg9iVFvIWR+GxXUvpes7FDN0WfXa3l7wtGOw==" + }, + "node_modules/robust-determinant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/robust-determinant/-/robust-determinant-1.1.0.tgz", + "integrity": "sha512-xva9bx/vyAv3pVYL2++vlnvM9q7oQOeCS5iscmlWtmaXHEgI4GFWeuYPUVVhvmYwx9N49EsQTonVJihYtcMo1Q==", + "dependencies": { + "robust-compress": "^1.0.0", + "robust-scale": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.0" + } + }, + "node_modules/robust-dot-product": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-dot-product/-/robust-dot-product-1.0.0.tgz", + "integrity": "sha512-Nu/wah8B8RotyZLRPdlEL0ZDh3b7wSwUBLdbTHwS/yw0qqjMJ943PSCkd6EsF5R5QFDWF2x77DGsbmnv9/7/ew==", + "dependencies": { + "robust-sum": "^1.0.0", + "two-product": "^1.0.0" + } + }, + "node_modules/robust-in-sphere": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/robust-in-sphere/-/robust-in-sphere-1.2.1.tgz", + "integrity": "sha512-3zJdcMIOP1gdwux93MKTS0RiMYEGwQBoE5R1IW/9ZQmGeZzP7f7i4+xdcK8ujJvF/dEOS1WPuI9IB1WNFbj3Cg==", + "dependencies": { + "robust-scale": "^1.0.0", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.0" + } + }, + "node_modules/robust-linear-solve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-linear-solve/-/robust-linear-solve-1.0.0.tgz", + "integrity": "sha512-I1qW8Bl9+UYeGNh2Vt8cwkcD74xWMyjnU6lSVcZrf0eyfwPmreflY3v0SvqCZOj5ddxnSS1Xp31igbFNcg1TGQ==", + "dependencies": { + "robust-determinant": "^1.1.0" + } + }, + "node_modules/robust-orientation": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.2.1.tgz", + "integrity": "sha512-FuTptgKwY6iNuU15nrIJDLjXzCChWB+T4AvksRtwPS/WZ3HuP1CElCm1t+OBfgQKfWbtZIawip+61k7+buRKAg==", + "dependencies": { + "robust-scale": "^1.0.2", + "robust-subtract": "^1.0.0", + "robust-sum": "^1.0.0", + "two-product": "^1.0.2" + } + }, + "node_modules/robust-product": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-product/-/robust-product-1.0.0.tgz", + "integrity": "sha512-7ww6m+ICW6Dt7ylHVy1aeeNwTfMXfh2BHqHVNE+CHvrU9sI97Vb6uHnid0MN3I9afTI5DXOB7q4SQa2fxuo2Gw==", + "dependencies": { + "robust-scale": "^1.0.0", + "robust-sum": "^1.0.0" + } + }, + "node_modules/robust-scale": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", + "integrity": "sha512-jBR91a/vomMAzazwpsPTPeuTPPmWBacwA+WYGNKcRGSh6xweuQ2ZbjRZ4v792/bZOhRKXRiQH0F48AvuajY0tQ==", + "dependencies": { + "two-product": "^1.0.2", + "two-sum": "^1.0.0" + } + }, + "node_modules/robust-segment-intersect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/robust-segment-intersect/-/robust-segment-intersect-1.0.1.tgz", + "integrity": "sha512-QWngxcL7rCRLK7nTMcTNBPi/q+fecrOo6aOtTPnXjT/Dve5AK20DzUSq2fznUS+rCAxyir6OdPgDCzcUxFtJoQ==", + "dependencies": { + "robust-orientation": "^1.1.3" + } + }, + "node_modules/robust-subtract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", + "integrity": "sha512-xhKUno+Rl+trmxAIVwjQMiVdpF5llxytozXJOdoT4eTIqmqsndQqFb1A0oiW3sZGlhMRhOi6pAD4MF1YYW6o/A==" + }, + "node_modules/robust-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", + "integrity": "sha512-AvLExwpaqUqD1uwLU6MwzzfRdaI6VEZsyvQ3IAQ0ZJ08v1H+DTyqskrf2ZJyh0BDduFVLN7H04Zmc+qTiahhAw==" + }, + "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==", + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sane-topojson": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-4.0.0.tgz", + "integrity": "sha512-bJILrpBboQfabG3BNnHI2hZl52pbt80BE09u4WhnrmzuF2JbMKZdl62G5glXskJ46p+gxE2IzOwGj/awR4g8AA==" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "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==", + "dev": true, + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallow-clone/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/signum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", + "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/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/simplicial-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz", + "integrity": "sha512-mHauIKSOy3GquM5VnYEiu7eP5y4A8BiaN9ezUUgyYFz1k68PqDYcyaH3kenp2cyvWZE96QKE3nrxYw65Allqiw==", + "dependencies": { + "bit-twiddle": "^1.0.0", + "union-find": "^1.0.0" + } + }, + "node_modules/simplicial-complex-boundary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simplicial-complex-boundary/-/simplicial-complex-boundary-1.0.1.tgz", + "integrity": "sha512-hz/AaVbs+s08EVoxlbCE68AlC6/mxFJLxJrGRMbDoTjz3030nhcOq+w5+f0/ZaU2EYjmwa8CdVKpiRVIrhaZjA==", + "dependencies": { + "boundary-cells": "^2.0.0", + "reduce-simplicial-complex": "^1.0.0" + } + }, + "node_modules/simplicial-complex-contour": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/simplicial-complex-contour/-/simplicial-complex-contour-1.0.2.tgz", + "integrity": "sha512-Janyqvpa7jgr9MJbwR/XGyYz7bdhXNq7zgHxD0G54LCRNyn4bf3Hely2iWQeK/IGu3c5BaWFUh7ElxqXhKrq0g==", + "dependencies": { + "marching-simplex-table": "^1.0.0", + "ndarray": "^1.0.15", + "ndarray-sort": "^1.0.0", + "typedarray-pool": "^1.1.0" + } + }, + "node_modules/simplify-planar-graph": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/simplify-planar-graph/-/simplify-planar-graph-2.0.1.tgz", + "integrity": "sha512-KdC2ZPFvrGl9+lH/P3Yik7G0si2Zpk6Xiqjq8l9U1lOox5a/9dGLjevi9tvqoh4V7yQbs7fs6+rNCOAdrzUktw==", + "dependencies": { + "robust-orientation": "^1.0.1", + "simplicial-complex": "^0.3.3" + } + }, + "node_modules/simplify-planar-graph/node_modules/bit-twiddle": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-0.0.2.tgz", + "integrity": "sha512-76iFAOrkcuw5UPA30Pt32XaytMHXz/04JembgIwsQAp7ImHYSWNq1shBbrlWf6CUvh1+amQ81LI8hNhqQgsBEw==" + }, + "node_modules/simplify-planar-graph/node_modules/simplicial-complex": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-0.3.3.tgz", + "integrity": "sha512-JFSxp7I5yORuKSuwGN96thhkqZVvYB4pkTMkk+PKP2QsOYYU1e84OBoHwOpFyFmjyvB9B3UDZKzHQI5S/CPUPA==", + "dependencies": { + "bit-twiddle": "~0.0.1", + "union-find": "~0.0.3" + } + }, + "node_modules/simplify-planar-graph/node_modules/union-find": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/union-find/-/union-find-0.0.4.tgz", + "integrity": "sha512-207oken6EyGDCBK5l/LTPsWfgy8N8s6idwRK2TG0ssWhzPlxEDdBA8nIV+eLbkEMdA8pAwE8F7/xwv2sCESVjQ==" + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "node_modules/slab-decomposition": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.3.tgz", + "integrity": "sha512-1EfR304JHvX9vYQkUi4AKqN62mLsjk6W45xTk/TxwN8zd3HGwS7PVj9zj0I6fgCZqfGlimDEY+RzzASHn97ZmQ==", + "dependencies": { + "binary-search-bounds": "^2.0.0", + "functional-red-black-tree": "^1.0.0", + "robust-orientation": "^1.1.3" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/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==", + "dev": true + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split-polygon": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split-polygon/-/split-polygon-1.0.0.tgz", + "integrity": "sha512-nBFcgQUVEE8dcOjuKaRdlM53k8RxUYpRxZ//n0pHJQGhbVscrsti+gllJI3pK3y7fgFwGWgt7NFhAX5sz0UoWQ==", + "dependencies": { + "robust-dot-product": "^1.0.0", + "robust-sum": "^1.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/static-eval/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/static-eval/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "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-eval/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "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-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-split-by": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz", + "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==", + "dependencies": { + "parenthesis": "^3.1.5" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strongly-connected-components": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", + "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA==" + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/sugarss": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-3.0.3.tgz", + "integrity": "sha512-uxa2bbuc+w7ov7DyYIhF6bM0qZF3UkFT5/nE8AJgboiVnKsBDbwxs++dehEIe1JNhpMaGJc37wGQ2QrrWey2Sg==", + "dev": true, + "dependencies": { + "postcss": "^8.1.6" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "dependencies": { + "kdbush": "^3.0.0" + } + }, + "node_modules/superscript-text": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz", + "integrity": "sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==" + }, + "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/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/surface-nets": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz", + "integrity": "sha512-Se+BaCb5yc8AV1IfT6TwTWEe/KuzzjzcMQQCbcIahzk9xRO5bIxxGM2MmKxE9nmq8+RD8DLBLXu0BjXoRs21iw==", + "dependencies": { + "ndarray-extract-contour": "^1.0.0", + "triangulate-hypercube": "^1.0.0", + "zero-crossings": "^1.0.0" + } + }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==" + }, + "node_modules/svg-path-bounds": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz", + "integrity": "sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ==", + "dependencies": { + "abs-svg-path": "^0.1.1", + "is-svg-path": "^1.0.1", + "normalize-svg-path": "^1.0.0", + "parse-svg-path": "^0.1.2" + } + }, + "node_modules/svg-path-bounds/node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, + "node_modules/svg-path-sdf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/svg-path-sdf/-/svg-path-sdf-1.1.3.tgz", + "integrity": "sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg==", + "dependencies": { + "bitmap-sdf": "^1.0.0", + "draw-svg-path": "^1.0.0", + "is-svg-path": "^1.0.1", + "parse-svg-path": "^0.1.2", + "svg-path-bounds": "^1.0.1" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/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==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/svgo/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==", + "dev": true + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.6.tgz", + "integrity": "sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ==", + "dependencies": { + "@fullhuman/postcss-purgecss": "^2.1.2", + "autoprefixer": "^9.4.5", + "browserslist": "^4.12.0", + "bytes": "^3.0.0", + "chalk": "^3.0.0 || ^4.0.0", + "color": "^3.1.2", + "detective": "^5.2.0", + "fs-extra": "^8.0.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.20", + "node-emoji": "^1.8.1", + "normalize.css": "^8.0.1", + "object-hash": "^2.0.3", + "postcss": "^7.0.11", + "postcss-functions": "^3.0.0", + "postcss-js": "^2.0.0", + "postcss-nested": "^4.1.1", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "reduce-css-calc": "^2.1.6", + "resolve": "^1.14.2" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "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/ansi-styles/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/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "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": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "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/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/tailwindcss/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/tailwindcss/node_modules/postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, + "node_modules/tailwindcss/node_modules/postcss-nested": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", + "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", + "dependencies": { + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/tailwindcss/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/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/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", + "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/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/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", + "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.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==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-cache": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.2.2.tgz", + "integrity": "sha512-zky+UDYiX0a/aPw/YTBD+EzKMlCTu1chFuCMZeAkgoRiceySdROu1V2kJXhCbtEdBhiOviYnAdGiSYl58HW0ZQ==", + "dependencies": { + "vectorize-text": "^3.2.1" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/three": { + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz", + "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-float32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.1.0.tgz", + "integrity": "sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg==" + }, + "node_modules/to-px": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.0.1.tgz", + "integrity": "sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw==", + "dependencies": { + "parse-unit": "^1.0.1" + } + }, + "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==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "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/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/triangulate-hypercube": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/triangulate-hypercube/-/triangulate-hypercube-1.0.1.tgz", + "integrity": "sha512-SAIacSBfUNfgeCna8q2i+1taOtFJkYuOqpduaJ1KUeOJpqc0lLKMYzPnZb4CA6KCOiD8Pd4YbuVq41wa9dvWyw==", + "dependencies": { + "gamma": "^0.1.0", + "permutation-parity": "^1.0.0", + "permutation-rank": "^1.0.0" + } + }, + "node_modules/triangulate-polyline": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz", + "integrity": "sha512-crJcVFtVPFYQ8r9iIhe9JqkauDvNWDSZLot8ly3DniSCO+zyUfKbtfD3fEoBaA5uMrQU/zBi11NBuVQeSToToQ==", + "dependencies": { + "cdt2d": "^1.0.0" + } + }, + "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": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/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": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "dev": true + }, + "node_modules/turntable-camera-controller": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/turntable-camera-controller/-/turntable-camera-controller-3.0.1.tgz", + "integrity": "sha512-UOGu9W/Mx053pAaczi0BEPqvWJOqSgtpdigWG9C8dX8rQVdyl2hWmpdJW3m15QrGxJtJHIhhDTHVtTZzPkd/FA==", + "dependencies": { + "filtered-vector": "^1.2.1", + "gl-mat4": "^1.0.2", + "gl-vec3": "^1.0.2" + } + }, + "node_modules/two-product": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", + "integrity": "sha512-vOyrqmeYvzjToVM08iU52OFocWT6eB/I5LUWYnxeAPGXAhAxXYU/Yr/R2uY5/5n4bvJQL9AQulIuxpIsMoT8XQ==" + }, + "node_modules/two-sum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", + "integrity": "sha512-phP48e8AawgsNUjEY2WvoIWqdie8PoiDZGxTDv70LDr01uX5wLEQbOgSP7Z/B6+SW5oLtbe8qaYX2fKJs3CGTw==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/typedarray-pool": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.2.0.tgz", + "integrity": "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==", + "dependencies": { + "bit-twiddle": "^1.0.0", + "dup": "^1.0.0" + } + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "node_modules/uglify-js/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglymol": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/uglymol/-/uglymol-0.6.4.tgz", + "integrity": "sha512-ZjGNsJbLUt42ETlC7I7yjGPNR6OzdzYeCrefslVHHdEgFsaPKbKtA6OaSzyGlT6zyNuzYYK6uEDuPtgBYfyAuA==" + }, + "node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg==" + }, + "node_modules/underscore-template-loader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/underscore-template-loader/-/underscore-template-loader-1.2.0.tgz", + "integrity": "sha512-PSlJNJRlF0YtxnipqMVckZMkjxG3dDnGbDWc1G7UrSWI02iFQGGNEVOAgrgGbOhswXVx1frzA9b6Uyxs627ojg==", + "dev": true, + "dependencies": { + "fastparse": "^1.1.2", + "loader-utils": "^2.0.0" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/union-find": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz", + "integrity": "sha512-wFA9bMD/40k7ZcpKVXfu6X1qD3ri5ryO8HUsuA1RnxPCQl66Mu6DgkxyR+XNnd+osD0aLENixcJVFj+uf+O4gw==" + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz", + "integrity": "sha512-rCiBPiHxZwT4+sBhEbChzpO5hYHjm91kScWgdHf4Qeafs6Ba7MBl+d9GlGv72bcTZQO0sLmtQS1pHSWoCLtN/A==" + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/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==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vectorize-text": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.2.2.tgz", + "integrity": "sha512-34NVOCpMMQVXujU4vb/c6u98h6djI0jGdtC202H4Huvzn48B6ARsR7cmGh1xsAc0pHNQiUKGK/aHF05VtGv+eA==", + "dependencies": { + "cdt2d": "^1.0.0", + "clean-pslg": "^1.1.0", + "ndarray": "^1.0.11", + "planar-graph-to-polyline": "^1.0.6", + "simplify-planar-graph": "^2.0.1", + "surface-nets": "^1.0.0", + "triangulate-polyline": "^1.0.0" + } + }, + "node_modules/vee-validate": { + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-2.2.15.tgz", + "integrity": "sha512-4TOsI8XwVkKVLkg8Nhmy+jyoJrR6XcTRDyxBarzcCvYzU61zamipS1WsB6FlDze8eJQpgglS4NXAS6o4NDPs1g==" + }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "node_modules/vue": { + "version": "2.7.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", + "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", + "dependencies": { + "@vue/compiler-sfc": "2.7.14", + "csstype": "^3.1.0" + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz", + "integrity": "sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vue-eslint-parser/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/vue-eslint-parser/node_modules/semver": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-eslint-parser/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/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "node_modules/vue-loader": { + "version": "15.10.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.1.tgz", + "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==", + "dev": true, + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/vue-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-router": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz", + "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==" + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", + "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "node_modules/vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "peerDependencies": { + "vue": "^2.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/weak-map": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", + "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==" + }, + "node_modules/weakmap-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/weakmap-shim/-/weakmap-shim-1.1.1.tgz", + "integrity": "sha512-/wNyG+1FpiHhnfQo+TuA/XAUpvOOkKVl0A4qpT+oGcj5SlZCLmM+M1Py/3Sj8sy+YrEauCVITOxCsZKo6sPbQg==" + }, + "node_modules/webgl-context": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz", + "integrity": "sha512-q/fGIivtqTT7PEoF07axFIlHNk/XCPaYpq64btnepopSWvKNFkoORlQYgqDigBIuGA1ExnFd/GnSUnBNEPQY7Q==", + "dependencies": { + "get-canvas-context": "^1.0.1" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.88.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", + "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz", + "integrity": "sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "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/world-calendars": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.3.tgz", + "integrity": "sha512-sAjLZkBnsbHkHWVhrsCU5Sa/EVuf9QqgvrN8zyJ2L/F9FR9Oc6CvVK0674+PGAtmmmYQMH98tCUSO4QLQv3/TQ==", + "dependencies": { + "object-assign": "^4.1.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "peer": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "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/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "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==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zero-crossings": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz", + "integrity": "sha512-iNIldMZaDtAyIJMJ8NnGVHeejH//y4eVmpXriM+q/B/BPNz+2E7oAgSnw9MXqCd3RbQ8W+hor7T2jEyRoc/s2A==", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/zlibjs": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.1.8.tgz", + "integrity": "sha512-K6TlrudzpjDHG6/uzyq8X9/2UFrcSLKhqxZc2nP1fc85vJdhPVehsyElowkI6sqYdF/QA3HA81oEBBLKXCyENA==", + "engines": { + "node": "*" + } + } + }, + "dependencies": { + "@achrinza/node-ipc": { + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.5.tgz", + "integrity": "sha512-kBX7Ay911iXZ3VZ1pYltj3Rfu7Ow9H7sK4H4RSfWIfWR2JKNB40K808wppoRIEzE2j2hXLU+r6TJgCAliCGhyQ==", + "dev": true, + "requires": { + "@node-ipc/js-queue": "2.0.3", + "event-pubsub": "4.3.0", + "js-message": "1.0.7" + } + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", + "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", + "dev": true, + "peer": true + }, + "@babel/core": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", + "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", + "dev": true, + "peer": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.1", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.19.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", + "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", + "dev": true, + "peer": true, + "requires": { + "@babel/compat-data": "^7.19.1", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "peer": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "peer": true + }, + "@babel/helpers": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==" + }, + "@babel/runtime-corejs2": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.19.2.tgz", + "integrity": "sha512-XofFIUXoVSuIabaED6ZNzvQ5iFzwQ3rnflnJTlMfmaZ9MHi+fmPk0iAwA6J6+8tC2UVshfyWPxIDmTKR+fRofA==", + "dev": true, + "requires": { + "core-js": "^2.6.12", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz", + "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.1", + "@babel/types": "^7.19.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@choojs/findup": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz", + "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==", + "requires": { + "commander": "^2.15.1" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@fullhuman/postcss-purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", + "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", + "requires": { + "postcss": "7.0.32", + "purgecss": "^2.3.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "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" + } + } + } + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@highcharts/map-collection": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@highcharts/map-collection/-/map-collection-1.1.4.tgz", + "integrity": "sha512-JzoBLWFJSjzjUV/m+eU2FbJBeQCfheyFJBUyQ9xyUhSs8yqUbE/JN0x9VZe7lDr71MfTO+bO50YpCrk5BTRCzA==" + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "requires": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + } + } + }, + "@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==" + }, + "@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" + }, + "@mapbox/mapbox-gl-supported": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", + "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", + "requires": {} + }, + "@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "@mapbox/tiny-sdf": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", + "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==" + }, + "@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" + }, + "@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "requires": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + }, + "@node-ipc/js-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", + "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", + "dev": true, + "requires": { + "easy-stack": "1.0.1" + } + }, + "@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==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@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==", + "dev": true + }, + "@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==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@plotly/d3-sankey": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz", + "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==", + "requires": { + "d3-array": "1", + "d3-collection": "1", + "d3-shape": "^1.2.0" + }, + "dependencies": { + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + } + } + }, + "@plotly/d3-sankey-circular": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", + "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", + "requires": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.4", + "d3-shape": "^1.2.0", + "elementary-circuits-directed-graph": "^1.0.4" + }, + "dependencies": { + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + } + } + }, + "@plotly/point-cluster": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@plotly/point-cluster/-/point-cluster-3.1.9.tgz", + "integrity": "sha512-MwaI6g9scKf68Orpr1pHZ597pYx9uP8UEFXLPbsCmuw3a84obwz6pnMXGc90VhgDNeNiLEdlmuK7CPo+5PIxXw==", + "requires": { + "array-bounds": "^1.0.1", + "binary-search-bounds": "^2.0.4", + "clamp": "^1.0.1", + "defined": "^1.0.0", + "dtype": "^2.0.0", + "flatten-vertex-data": "^1.0.2", + "is-obj": "^1.0.1", + "math-log2": "^1.0.1", + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true + }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "@soda/friendly-errors-webpack-plugin": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", + "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^3.0.0", + "error-stack-parser": "^2.0.6", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true + }, + "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, + "peer": true + }, + "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, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true, + "peer": true + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@turf/area": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.5.0.tgz", + "integrity": "sha512-xCZdiuojokLbQ+29qR6qoMD89hv+JAgWjLrwSEWL+3JV8IXKeNFl6XkEJz9HGkVpnXvQKJoRz4/liT+8ZZ5Jyg==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/bbox": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", + "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/centroid": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz", + "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==" + }, + "@turf/meta": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", + "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", + "requires": { + "@turf/helpers": "^6.5.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true, + "peer": true + }, + "@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "@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 + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "@vue/cli-overlay": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.8.tgz", + "integrity": "sha512-KmtievE/B4kcXp6SuM2gzsnSd8WebkQpg3XaB6GmFh1BJGRqa1UiW9up7L/Q67uOdTigHxr5Ar2lZms4RcDjwQ==", + "dev": true, + "peer": true + }, + "@vue/cli-plugin-router": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.8.tgz", + "integrity": "sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==", + "dev": true, + "peer": true, + "requires": { + "@vue/cli-shared-utils": "^5.0.8" + } + }, + "@vue/cli-plugin-unit-mocha": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-mocha/-/cli-plugin-unit-mocha-5.0.8.tgz", + "integrity": "sha512-t8eN1ttbSkHg+U7AjCKwpuhpV2OBsP35RjBkdSrcAPjoX3MxVwoWyqGPeYI+jWbPGX3tFxM/uMQwN0U3lmzH7A==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^5.0.8", + "jsdom": "^18.0.1", + "jsdom-global": "^3.0.2", + "mocha": "^8.3.0", + "mochapack": "^2.1.0" + } + }, + "@vue/cli-plugin-vuex": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz", + "integrity": "sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==", + "dev": true, + "peer": true, + "requires": {} + }, + "@vue/cli-service": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.8.tgz", + "integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-compilation-targets": "^7.12.16", + "@soda/friendly-errors-webpack-plugin": "^1.8.0", + "@soda/get-current-script": "^1.0.2", + "@types/minimist": "^1.2.0", + "@vue/cli-overlay": "^5.0.8", + "@vue/cli-plugin-router": "^5.0.8", + "@vue/cli-plugin-vuex": "^5.0.8", + "@vue/cli-shared-utils": "^5.0.8", + "@vue/component-compiler-utils": "^3.3.0", + "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", + "@vue/web-component-wrapper": "^1.3.0", + "acorn": "^8.0.5", + "acorn-walk": "^8.0.2", + "address": "^1.1.2", + "autoprefixer": "^10.2.4", + "browserslist": "^4.16.3", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.10", + "clipboardy": "^2.3.0", + "cliui": "^7.0.4", + "copy-webpack-plugin": "^9.0.1", + "css-loader": "^6.5.0", + "css-minimizer-webpack-plugin": "^3.0.2", + "cssnano": "^5.0.0", + "debug": "^4.1.1", + "default-gateway": "^6.0.3", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.2", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^5.1.0", + "is-file-esm": "^1.0.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "mini-css-extract-plugin": "^2.5.3", + "minimist": "^1.2.5", + "module-alias": "^2.2.2", + "portfinder": "^1.0.26", + "postcss": "^8.2.6", + "postcss-loader": "^6.1.1", + "progress-webpack-plugin": "^1.0.12", + "ssri": "^8.0.1", + "terser-webpack-plugin": "^5.1.1", + "thread-loader": "^3.0.0", + "vue-loader": "^17.0.0", + "vue-style-loader": "^4.1.3", + "webpack": "^5.54.0", + "webpack-bundle-analyzer": "^4.4.0", + "webpack-chain": "^6.5.1", + "webpack-dev-server": "^4.7.3", + "webpack-merge": "^5.7.3", + "webpack-virtual-modules": "^0.4.2", + "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true + }, + "copy-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==", + "dev": true, + "peer": true, + "requires": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "peer": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dev": true, + "peer": true, + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "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, + "peer": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "peer": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "peer": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "peer": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "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, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "vue-loader": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz", + "integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + } + } + } + }, + "@vue/cli-shared-utils": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", + "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==", + "dev": true, + "requires": { + "@achrinza/node-ipc": "^9.2.5", + "chalk": "^4.1.2", + "execa": "^1.0.0", + "joi": "^17.4.0", + "launch-editor": "^2.2.1", + "lru-cache": "^6.0.0", + "node-fetch": "^2.6.7", + "open": "^8.0.2", + "ora": "^5.3.0", + "read-pkg": "^5.1.1", + "semver": "^7.3.4", + "strip-ansi": "^6.0.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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" + } + } + } + }, + "@vue/compiler-sfc": { + "version": "2.7.10", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz", + "integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==", + "requires": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + } + }, + "@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2 || ^2.0.0", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + } + } + }, + "@vue/test-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.3.0.tgz", + "integrity": "sha512-Xk2Xiyj2k5dFb8eYUKkcN9PzqZSppTlx7LaQWBbdA8tqh3jHr/KHX2/YLhNFc/xwDrgeLybqd+4ZCPJSGPIqeA==", + "dev": true, + "requires": { + "dom-event-types": "^1.0.0", + "lodash": "^4.17.15", + "pretty": "^2.0.0" + } + }, + "@vue/vue-loader-v15": { + "version": "npm:vue-loader@15.10.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", + "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", + "dev": true, + "peer": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "peer": true + } + } + }, + "@vue/web-component-wrapper": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", + "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", + "dev": true, + "peer": true + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "requires": {} + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "3d-view": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.1.tgz", + "integrity": "sha512-YSLRHXNpSziaaiK2R0pI5+JKguoJVbtWmIv9YyBFtl0+q42kQwJB/JUulbFR/1zYFm58ifjKQ6kVdgZ6tyKtCA==", + "requires": { + "matrix-camera-controller": "^2.1.1", + "orbit-camera-controller": "^4.0.0", + "turntable-camera-controller": "^3.0.0" + } + }, + "a-big-triangle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz", + "integrity": "sha512-AboEtoSPueZisde3Vr+7VRSfUIWBSGZUOtW3bJrOZXgIyK7dNNDdpDmOKJjg5GmJLlRKUONWV8lMgTK8MBhQWw==", + "requires": { + "gl-buffer": "^2.1.1", + "gl-vao": "^1.2.0", + "weak-map": "^1.0.5" + } + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "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==", + "dev": true + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "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==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "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==" + } + } + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "add-line-numbers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz", + "integrity": "sha512-w+2a1malCvWwACQFBpZ5/uwmHGaGYT+aGIxA8ONF5vlhe6X/gD3eR8qVoLWa+5nnWAOq2LuPbrqDYqj1pn0WMg==", + "requires": { + "pad-left": "^1.0.2" + } + }, + "address": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", + "dev": true, + "peer": true + }, + "affine-hull": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz", + "integrity": "sha512-3QNG6+vFAwJvSZHsJYDJ/mt1Cxx9n5ffA+1Ohmj7udw0JuRgUVIXK0P9N9pCMuEdS3jCNt8GFX5q2fChq+GO3Q==", + "requires": { + "robust-orientation": "^1.1.3" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "almost-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/almost-equal/-/almost-equal-1.1.0.tgz", + "integrity": "sha512-0V/PkoculFl5+0Lp47JoxUcO0xSxhIBvm+BxHdD/OgXNmdRpRHCFnKVuUoWyS9EzQP+otSGv0m9Lb4yVkQBn2A==" + }, + "alpha-complex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/alpha-complex/-/alpha-complex-1.0.0.tgz", + "integrity": "sha512-rhsjKfc9tMF5QZc0NhKz/zFzMu2rvHxCP/PyJtEmMkV7M848YjIoQGDlNGp+vTqxXjA8wAY2OxgR1K54C2Awkg==", + "requires": { + "circumradius": "^1.0.0", + "delaunay-triangulate": "^1.1.6" + } + }, + "alpha-shape": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz", + "integrity": "sha512-/V+fmmjtSA2yfQNq8iEqBxnPbjcOMXpM9Ny+yE/O7aLR7Q1oPzUc9bHH0fPHS3hUugUL/dHzTis6l3JirYOS/w==", + "requires": { + "alpha-complex": "^1.0.0", + "simplicial-complex-boundary": "^1.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "peer": true + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "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==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "peer": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "peer": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "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==" + }, + "array-bounds": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz", + "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-normalize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", + "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", + "requires": { + "array-bounds": "^1.0.0" + } + }, + "array-range": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz", + "integrity": "sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA==" + }, + "array-rearrange": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz", + "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==" + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "peer": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "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==", + "dev": true, + "peer": true + }, + "atob-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz", + "integrity": "sha512-ArXcmHR/vwSN37HLVap/Y5SKpz12CuEybxe1sIYl7th/S6SQPrVMNFt6rblJzCOAxn0SHbXpknUtqbAIeo3Aow==" + }, + "autoprefixer": { + "version": "10.4.11", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.11.tgz", + "integrity": "sha512-5lHp6DgRodxlBLSkzHOTcufWFflH1ewfy2hvFQyjrblBFlP/0Yh4O/Wrg4ow8WRlN3AAUFFLAQwX8hTptzqVHg==", + "dev": true, + "requires": { + "browserslist": "^4.21.3", + "caniuse-lite": "^1.0.30001399", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "backbone": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.1.2.tgz", + "integrity": "sha512-bdiYFVF+mXQ3712Urje2uvYClLPXOd2jsvp8AHlPyMKqpHRPCmCxZf099kCRadhA2sMxp761XB2aZ0/HYF4fDg==", + "requires": { + "underscore": ">=1.5.0" + } + }, + "backbone-validation": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/backbone-validation/-/backbone-validation-0.9.1.tgz", + "integrity": "sha512-bUdbJ8S3BvdLqxM8ndsaVXFNOa13W2ODCH0pQ9qYSRvFwEMehKn2edzeJgKyoS9trWcI0F94t7hpFL/7HzoY3A==", + "requires": { + "backbone": ">=1.0.0", + "underscore": ">=1.4.3" + } + }, + "backbone.babysitter": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/backbone.babysitter/-/backbone.babysitter-0.1.12.tgz", + "integrity": "sha512-1zhTnN/QrnbUWrptAxKYL+ABEeyNPbr654PErBNxIYjKbktEcPeZz/dojcP3Id5PUCO/ldo334yTywNcpGl/oA==", + "requires": { + "backbone": ">=0.9.9 <=1.3.x", + "underscore": ">=1.4.0 <=1.8.3" + } + }, + "backbone.marionette": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/backbone.marionette/-/backbone.marionette-2.1.0.tgz", + "integrity": "sha512-04amIUEE9rWIQ6Do8TGHmTju6OByBxP/fkgsOtcmsDovDCUFPVLXJGEasbDFy1chL8xs4xix+ESrumd23/PR5Q==", + "requires": { + "backbone": "1.0.0 - 1.1.2", + "backbone.babysitter": "^0.1.0", + "backbone.wreqr": "^1.0.0", + "underscore": "1.4.4 - 1.6.0" + } + }, + "backbone.paginator": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.0.tgz", + "integrity": "sha512-v+KVUz3Jlf593ecIzor/BzuLczuhFYwJoAVKogQ6NGF+Q5BdExO3TczS5OD49WOi2pJ0rrYgBeAjAxkCuI+sdA==", + "requires": { + "backbone": "^1.1.2", + "underscore": "^1.5.0" + } + }, + "backbone.syphon": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/backbone.syphon/-/backbone.syphon-0.5.0.tgz", + "integrity": "sha512-L5hlhyjbZsQZr7UE7UQBpxVl2iDLBNczTGlYDgqaK8EZaxm0jt+9qjVQxYXQ3TsThmpDk/gP5kkla5seiSn21g==", + "requires": { + "backbone": "^1.1.0", + "jquery": "^1.8.0 || ^2.1.0", + "underscore": "^1.6.0" + } + }, + "backbone.wreqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/backbone.wreqr/-/backbone.wreqr-1.4.0.tgz", + "integrity": "sha512-r5r71OxIRYiesUa6CNgD2zjChizsUz6rltthjMJeS+YTYTC7UlLvkEb9PMsBUrrbuoiZ9vXL2Znro94OlL7UfA==", + "requires": { + "backbone": ">=0.9.9 <=1.3.x", + "underscore": ">=1.3.3 <=1.8.3" + } + }, + "backgrid": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/backgrid/-/backgrid-0.3.8.tgz", + "integrity": "sha512-Klzo941ahoj8Kqd0tRsau+VfXddV3YnQTwb6wVwIaaQxoJ9ORykQy2MNit1MUBnZO6IValYJPvCQyvZhnV6Lfg==", + "requires": { + "backbone": "1.1.2 || 1.2.3 || ~1.3.2", + "underscore": "^1.8.0" + }, + "dependencies": { + "underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" + } + } + }, + "backgrid-paginator": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/backgrid-paginator/-/backgrid-paginator-0.3.9.tgz", + "integrity": "sha512-Y8nhpFoXZ4dk4eJ2wuwPeC1Uekq3H/0tLvZA48Ux+2tpMq3oPOlMv41EEghj6z1jWbvRAzwxuiyQXfQ2EpYEvA==", + "requires": { + "backbone": "1.1.2 || 1.2.3 || ~1.3.2", + "backbone.paginator": "^2.0.5", + "backgrid": "~0.3.7", + "underscore": "^1.8.0" + }, + "dependencies": { + "backbone.paginator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.8.tgz", + "integrity": "sha512-8XS2CTbjnwMbJ/3Traa1te2RPecOGbZ9tc52T89pzo6NXlVEJDFnC++dp7CQLBUZpgk3g0veX8mUbEF4wbD2NQ==", + "requires": { + "backbone": "1.1.2 || 1.2.3 || ^1.3.2", + "underscore": "^1.8.0" + } + }, + "underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" + } + } + }, + "backgrid-select-all": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/backgrid-select-all/-/backgrid-select-all-0.3.5.tgz", + "integrity": "sha512-bwMQi5d8AnBSZDiV4nWrXcOSmEODbxB6/70mSHG8cGoDfjgW5X7mLiXlmlgEP3VsA1avFD6VvCvpAKZ4BS5f9Q==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "barycentric": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz", + "integrity": "sha512-47BuWXsenBbox4q1zqJrUoxq1oM1ysrYc5mdBACAwaP+CL+tcNauC3ybA0lzbIWzJCLZYMqebAx46EauTI2Nrg==", + "requires": { + "robust-linear-solve": "^1.0.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big-rat": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz", + "integrity": "sha512-AubEohDDrak6urvKkFMIlwPWyQbJ/eq04YsK/SNipH7NNiPCYchjQNvWYK5vyyMmtGXAmNmsAjIcfkaDuTtd8g==", + "requires": { + "bit-twiddle": "^1.0.2", + "bn.js": "^4.11.6", + "double-bits": "^1.1.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "binary-search-bounds": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", + "integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==" + }, + "bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==" + }, + "bitmap-sdf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.4.tgz", + "integrity": "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "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==" + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "boundary-cells": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/boundary-cells/-/boundary-cells-2.0.2.tgz", + "integrity": "sha512-/S48oUFYEgZMNvdqC87iYRbLBAPHYijPRNrNpm/sS8u7ijIViKm/hrV3YD4sx/W68AsG5zLMyBEditVHApHU5w==" + }, + "box-intersect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/box-intersect/-/box-intersect-1.0.2.tgz", + "integrity": "sha512-yJeMwlmFPG1gIa7Rs/cGXeI6iOj6Qz5MG5PE61xLKpElUGzmJ4abm+qsLpzxKJFpsSDq742BQEocr8dI2t8Nxw==", + "requires": { + "bit-twiddle": "^1.0.2", + "typedarray-pool": "^1.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "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==", + "dev": true + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "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==" + }, + "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==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001407", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001407.tgz", + "integrity": "sha512-4ydV+t4P7X3zH83fQWNDX/mQEzYomossfpViCOx9zHBSMV+rIe3LFqglHHtVyvNl1FhTNxPxs3jei82iqOW04w==" + }, + "canvas-fit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz", + "integrity": "sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==", + "requires": { + "element-size": "^1.1.1" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "peer": true + }, + "cdt2d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cdt2d/-/cdt2d-1.0.0.tgz", + "integrity": "sha512-pFKb7gVhpsI6onS5HUXRoqbBIJB4CJ+KPk8kgaIVcm0zFgOxIyBT5vzifZ4j1aoGVJS0U1A+S4oFDshuLAitlA==", + "requires": { + "binary-search-bounds": "^2.0.3", + "robust-in-sphere": "^1.1.3", + "robust-orientation": "^1.1.3" + } + }, + "cell-orientation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cell-orientation/-/cell-orientation-1.0.1.tgz", + "integrity": "sha512-DtEsrgP+donmPxpEZm7hK8zCPYDXAQ977ecJiE7G0gbTfnS6TZVBlief3IdRP/TZS1PVnJRGJTDdjSdV8mRDug==" + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ci/-/ci-2.2.0.tgz", + "integrity": "sha512-lBkEN6XclyW0jnprtFQ+dsbP+9zwmo37Z1cV38h4FSDgI2QzFqwknJnVSvRxK9UXkPC4ZcVOVFyCVrNylTX52Q==" + }, + "circumcenter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/circumcenter/-/circumcenter-1.0.0.tgz", + "integrity": "sha512-YRw0mvttcISviaOtSmaHb2G3ZVbkxzYPQeAEd57/CFFtmOkwfRTw9XuxYZ7PCi2BYa0NajjHV6bq4nbY1VCC8g==", + "requires": { + "dup": "^1.0.0", + "robust-linear-solve": "^1.0.0" + } + }, + "circumradius": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/circumradius/-/circumradius-1.0.0.tgz", + "integrity": "sha512-5ltoQvWQzJiZjCVX9PBKgKt+nsuzOLKayqXMNllfRSqIp2L5jFpdanv1V6j27Ue7ACxlzmamlR+jnLy+NTTVTw==", + "requires": { + "circumcenter": "^1.0.0" + } + }, + "clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==" + }, + "clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clean-pslg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz", + "integrity": "sha512-bJnEUR6gRiiNi2n4WSC6yrc0Hhn/oQDOTzs6evZfPwEF/VKVXM6xu0F4n/WSBz7TjTt/ZK6I5snRM9gVKMVAxA==", + "requires": { + "big-rat": "^1.0.3", + "box-intersect": "^1.0.1", + "nextafter": "^1.0.0", + "rat-vec": "^1.1.1", + "robust-segment-intersect": "^1.0.1", + "union-find": "^1.0.2", + "uniq": "^1.0.1" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.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==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true, + "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==", + "dev": true, + "peer": true + }, + "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, + "peer": true + }, + "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, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true + }, + "clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "peer": true, + "requires": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==", + "dev": true, + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-alpha": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz", + "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==", + "requires": { + "color-parse": "^1.3.8" + } + }, + "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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz", + "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==", + "requires": { + "clamp": "^1.0.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-normalize": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz", + "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==", + "requires": { + "clamp": "^1.0.1", + "color-rgba": "^2.1.1", + "dtype": "^2.0.0" + } + }, + "color-parse": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz", + "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==", + "requires": { + "color-name": "^1.0.0", + "defined": "^1.0.0", + "is-plain-obj": "^1.1.0" + } + }, + "color-rgba": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.1.1.tgz", + "integrity": "sha512-VaX97wsqrMwLSOR6H7rU1Doa2zyVdmShabKrPEIFywLlHoibgD3QW9Dw6fSqM4+H/LfjprDNAUUW31qEQcGzNw==", + "requires": { + "clamp": "^1.0.1", + "color-parse": "^1.3.8", + "color-space": "^1.14.6" + } + }, + "color-space": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.16.0.tgz", + "integrity": "sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg==", + "requires": { + "hsluv": "^0.0.3", + "mumath": "^3.3.4" + } + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "colormap": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.2.tgz", + "integrity": "sha512-jDOjaoEEmA9AgA11B/jCSAvYE95r3wRoAyTf3LEHGiUVlNHJaL1mRkf5AyLSpQBVGfTEPwGEqCIzL+kgr2WgNA==", + "requires": { + "lerp": "^1.0.3" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "compare-angle": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/compare-angle/-/compare-angle-1.0.1.tgz", + "integrity": "sha512-adM1/bpLFQFquh0/Qr5aiOPuztoga/lCf2Z45s+Oydgzf18F3wBSkdHmcHMeig0bD+dDKlz52u1rLOAOqiyE5A==", + "requires": { + "robust-orientation": "^1.0.2", + "robust-product": "^1.0.0", + "robust-sum": "^1.0.0", + "signum": "^0.0.0", + "two-sum": "^1.0.0" + }, + "dependencies": { + "signum": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz", + "integrity": "sha512-nct2ZUmwemVxeuPY5h+JLpHGJvLCXXNahGVI7IB3a6Fy5baX9AGSb854HceYH4FBw4eGjoZfEo9YRfkGfKdZQA==" + } + } + }, + "compare-cell": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/compare-cell/-/compare-cell-1.0.0.tgz", + "integrity": "sha512-uNIkjiNLZLhdCgouF39J+W04R7oP1vwrNME4vP2b2/bAa6PHOj+h8yXu52uPjPTKs5RatvqNsDVwEN7Yp19vNA==" + }, + "compare-oriented-cell": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz", + "integrity": "sha512-9D7R2MQfsGGRskZAZF0TkJHt9eFNbFkZyVdVps+WUYxtRHgG77BLbieKgSkj7iEAb9PNDSU9QNa9MtigjQ3ktQ==", + "requires": { + "cell-orientation": "^1.0.1", + "compare-cell": "^1.0.0" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "compute-dims": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dims/-/compute-dims-1.1.0.tgz", + "integrity": "sha512-YHMiIKjH/8Eom8zATk3g8/lH3HxGCZcVQyEfEoVrfWI7od/WRpTgRGShnei3jArYSx77mQqPxZNokjGHCdLfxg==", + "requires": { + "utils-copy": "^1.0.0", + "validate.io-array": "^1.0.6", + "validate.io-matrix-like": "^1.0.2", + "validate.io-ndarray-like": "^1.0.0", + "validate.io-positive-integer": "^1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "condense-newlines": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", + "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-whitespace": "^0.3.0", + "kind-of": "^3.0.2" + } + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "const-max-uint32": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/const-max-uint32/-/const-max-uint32-1.0.2.tgz", + "integrity": "sha512-T8/9bffg5RThuejasJWrwqxs3Q0fsJvyl7/33IB6svroD8JC93E7X60AuuOnDE8RlP6Jlb5FxmlrVDpl9KiU2Q==" + }, + "const-pinf-float64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/const-pinf-float64/-/const-pinf-float64-1.0.0.tgz", + "integrity": "sha512-wfs+V4HdSN7C3CWJWR7hVa24yTPn3mDJthwhRIObZBh6UjTjkUMUrCP3UrNGozB/HjTpcScnGXtQUNa+yjsIJQ==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "peer": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "convex-hull": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz", + "integrity": "sha512-24rZAoh81t41GHPLAxcsokgjH9XNoVqU2OiSi8iMHUn6HUURfiefcEWAPt1AfwZjBBWTKadOm1xUcUMnfFukhQ==", + "requires": { + "affine-hull": "^1.0.0", + "incremental-convex-hull": "^1.0.1", + "monotone-convex-hull-2d": "^1.0.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "copy-webpack-plugin": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz", + "integrity": "sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "fast-glob": "^3.2.4", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@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" + } + }, + "country-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz", + "integrity": "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA==" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "css-color-function": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", + "integrity": "sha512-YD/WhiRZIYgadwFJ48X5QmlOQ/w8Me4yQI6/eSUoiE8spIFp+S/rGpsAH48iyq/0ZWkCDWqVQKUlQmUzn7BQ9w==", + "dev": true, + "requires": { + "balanced-match": "0.1.0", + "color": "^0.11.0", + "debug": "^3.1.0", + "rgb": "~0.1.0" + }, + "dependencies": { + "balanced-match": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.1.0.tgz", + "integrity": "sha512-4xb6XqAEo3Z+5pEDJz33R8BZXI8FRJU+cDNLdKgDpmnz+pKKRVYLpdv+VvUAC7yUhBMj4izmyt19eCGv1QGV7A==", + "dev": true + }, + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "dev": true, + "requires": {} + }, + "css-font": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz", + "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==", + "requires": { + "css-font-size-keywords": "^1.0.0", + "css-font-stretch-keywords": "^1.0.1", + "css-font-style-keywords": "^1.0.1", + "css-font-weight-keywords": "^1.0.0", + "css-global-keywords": "^1.0.1", + "css-system-font-keywords": "^1.0.0", + "pick-by-alias": "^1.2.0", + "string-split-by": "^1.0.0", + "unquote": "^1.1.0" + } + }, + "css-font-size-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz", + "integrity": "sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==" + }, + "css-font-stretch-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz", + "integrity": "sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==" + }, + "css-font-style-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz", + "integrity": "sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==" + }, + "css-font-weight-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz", + "integrity": "sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==" + }, + "css-global-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz", + "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ==" + }, + "css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-Zd+yz4nta4GXi3pMqF6skO8kjzuCUbr62z8SLMGZZtxWxTGTLopOiabPGNDEyjHCRhnhdA1EfHmqLa2Oekjtng==", + "dev": true, + "requires": { + "cssnano": "^5.1.8", + "jest-worker": "^27.5.1", + "postcss": "^8.4.13", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-system-font-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", + "integrity": "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==" + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "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==" + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz", + "integrity": "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==", + "dev": true, + "requires": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "dev": true, + "requires": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "csv-file-validator": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/csv-file-validator/-/csv-file-validator-1.13.1.tgz", + "integrity": "sha512-2b1GRaTFlNN5pgnEBFpfDAfZK4mJ2Wvni8iJpkqte0wCUAHt1qrsZgchDODqM8gl/ZZ80TgPtO3VWXVLwHOyTw==", + "requires": { + "famulus": "^2.2.3", + "lodash": "^4.17.21", + "papaparse": "^5.3.1" + } + }, + "cubic-hermite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz", + "integrity": "sha512-DKZ6yLcJiJJgl54mGA4n0uueYB4qdPfOJrQ1HSEZqdKp6D25AAAWVDwpoAxLflOku5a/ALBO77oEIyWcVa+UYg==" + }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "d3": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", + "integrity": "sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg==" + }, + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" + }, + "d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "requires": { + "d3-color": "1 - 2" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + }, + "d3-scale": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", + "requires": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" + } + }, + "d3-scale-chromatic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz", + "integrity": "sha512-LLqy7dJSL8yDy7NRmf6xSlsFZ6zYvJ4BcWFE4zBrOPnQERv9zj24ohnXKRbyi9YHnYV+HN1oEO3iFK971/gkzA==", + "requires": { + "d3-color": "1 - 2", + "d3-interpolate": "1 - 2" + } + }, + "d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==" + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "requires": { + "d3-array": "2" + } + }, + "d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "requires": { + "d3-time": "1 - 2" + } + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + }, + "date-fns-tz": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.7.tgz", + "integrity": "sha512-1t1b8zyJo+UI8aR+g3iqr5fkUHWpd58VBx8J/ZSQ+w7YrGlw80Ag4sA86qkfCXRBLmMc4I2US+aPMd4uKvwj5g==", + "requires": {} + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "decimal.js": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.1.tgz", + "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true, + "peer": true + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" + }, + "delaunay-triangulate": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/delaunay-triangulate/-/delaunay-triangulate-1.1.6.tgz", + "integrity": "sha512-mhAclqFCgLoiBIDQDIz2K+puZq6OhYxunXrG2wtTcZS+S1xuzl+H3h0MIOajpES+Z+jfY/rz0wVt3o5iipt1wg==", + "requires": { + "incremental-convex-hull": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-kerning": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz", + "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-event-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.1.0.tgz", + "integrity": "sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==", + "dev": true + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + } + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "peer": true + }, + "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==", + "dev": true, + "peer": true + }, + "double-bits": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz", + "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg==" + }, + "draw-svg-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz", + "integrity": "sha512-P8j3IHxcgRMcY6sDzr0QvJDLzBnJJqpTG33UZ2Pvp8rw0apCHhJCWqYprqrXjrgHnJ6tuhP1iTJSAodPDHxwkg==", + "requires": { + "abs-svg-path": "~0.1.1", + "normalize-svg-path": "~0.1.0" + } + }, + "dtype": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz", + "integrity": "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==" + }, + "dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true + }, + "edges-to-adjacency-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz", + "integrity": "sha512-0n0Z+xTLfg96eYXm91PEY4rO4WGxohLWjJ9qD1RI3fzxKU6GHez+6KPajpobR4zeZxp7rSiHjHG5dZPj8Kj58Q==", + "requires": { + "uniq": "^1.0.0" + } + }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "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==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.255", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.255.tgz", + "integrity": "sha512-H+mFNKow6gi2P5Gi2d1Fvd3TUEJlB9CF7zYaIV9T83BE3wP1xZ0mRPbNTm0KUjyd1QiVy7iKXuIcjlDtBQMiAQ==" + }, + "element-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", + "integrity": "sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==" + }, + "elementary-circuits-directed-graph": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.3.1.tgz", + "integrity": "sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==", + "requires": { + "strongly-connected-components": "^1.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "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==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "peer": true, + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", + "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.2", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "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==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha512-sziUVwcvQ+lOsrTyUY0Q11ilAPj+dy7AQ1E1MgSaHTaaAFTffaa08QSlGNU61iyVaroyb6nYdBV6oD7nzn6i8w==", + "dev": true, + "requires": { + "recast": "~0.11.12", + "through": "~2.3.6" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "3.2.6", - "bundled": true, + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, - "optional": true, "requires": { - "ms": "^2.1.1" + "@babel/highlight": "^7.10.4" } }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.7", - "bundled": true, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "optional": true, "requires": { - "minipass": "^2.6.0" + "color-convert": "^2.0.1" } }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "optional": true, "requires": { - "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" + "sprintf-js": "~1.0.2" } }, - "glob": { - "version": "7.1.6", - "bundled": true, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "optional": true, "requires": { - "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" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "has-unicode": { + "color-convert": { "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "optional": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "color-name": "~1.1.4" } }, - "ignore-walk": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.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==", + "dev": true }, - "inflight": { - "version": "1.0.6", - "bundled": true, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, - "optional": true, "requires": { - "number-is-nan": "^1.0.0" + "type-fest": "^0.20.2" } }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } + "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 }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true }, - "minipass": { - "version": "2.9.0", - "bundled": true, + "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==", "dev": true, - "optional": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, - "minizlib": { - "version": "1.3.3", - "bundled": true, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "optional": true, "requires": { - "minipass": "^2.9.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, - "optional": true, "requires": { - "minimist": "0.0.8" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, - "ms": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true }, - "needle": { - "version": "2.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, - "node-pre-gyp": { - "version": "0.14.0", - "bundled": true, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, - "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" + "lru-cache": "^6.0.0" } }, - "nopt": { - "version": "4.0.1", - "bundled": true, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "shebang-regex": "^3.0.0" } }, - "npm-bundled": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, - "npm-packlist": { - "version": "1.4.7", - "bundled": true, + "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, - "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "has-flag": "^4.0.0" } }, - "npmlog": { - "version": "4.1.2", - "bundled": true, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "prelude-ls": "^1.2.1" } }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true }, - "once": { - "version": "1.4.0", - "bundled": true, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "optional": true, "requires": { - "wrappy": "1" + "isexe": "^2.0.0" } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, + } + } + }, + "eslint-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-4.0.2.tgz", + "integrity": "sha512-EDpXor6lsjtTzZpLUn7KmXs02+nIjGcgees9BYjNkWra3jVq5vVa8IoCKgzT2M7dNNeoMBtaSG83Bd40N3poLw==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "loader-utils": "^2.0.0", + "object-hash": "^2.0.3", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "optional": true, "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } + "graceful-fs": "^4.1.6" } }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, - "optional": true, "requires": { - "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" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "rimraf": { + "schema-utils": { "version": "2.7.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, - "optional": true, "requires": { - "glob": "^7.1.3" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "eslint-plugin-backbone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-backbone/-/eslint-plugin-backbone-2.1.1.tgz", + "integrity": "sha512-z+vfSft/gjNQXuG9Gvyo0AYmlAXyS2e01MPKeHLTr2lSow3bOuFQDpJA3TwopgVAC2d3edMOH7aeNJIyTS4t3w==", + "dev": true, + "requires": {} + }, + "eslint-plugin-vue": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.5.1.tgz", + "integrity": "sha512-Y0sL2RY7Xc9S8kNih9lbwHIDmewUg9bfas6WSzsOWRgDXhIHKxRBZYNAnVcXBFfE+bMWHUA5GLChl7TcTYUI8w==", + "dev": true, + "requires": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^9.0.1", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, - "optional": true + "requires": { + "eslint-visitor-keys": "^2.0.0" + } }, "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, - "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "lru-cache": "^6.0.0" } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true }, - "string_decoder": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "optional": true, "requires": { - "ansi-regex": "^2.0.0" + "ms": "2.0.0" } }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, - "tar": { - "version": "4.4.13", - "bundled": true, + "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==", + "dev": true + } + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "extract-frustum-planes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz", + "integrity": "sha512-GivvxEMgjSNnB3e1mIMBlB5ogPB6XyEjOQRGG0SfYVVLtu1ntLGHLT1ly8+mE819dKBHBwnm9+UBCScjiMgppA==" + }, + "falafel": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz", + "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==", + "requires": { + "acorn": "^7.1.1", + "isarray": "^2.0.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "famulus": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/famulus/-/famulus-2.2.3.tgz", + "integrity": "sha512-tEh0NlWBtXSu1t/uY1eN7DQbXXcezPUp2/q25Scbc0h+Wivu9GHcdVnzlOqhD6hetpaj9CMhRm5InSQscM7FWQ==", + "requires": { + "lodash": "^4.17.20" + } + }, + "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==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "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" + } + }, + "fast-isnumeric": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.4.tgz", + "integrity": "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw==", + "requires": { + "is-string-blank": "^1.0.1" + } + }, + "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==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "peer": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, - "optional": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, + } + } + }, + "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==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filtered-vector": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.5.tgz", + "integrity": "sha512-5Vu6wdtQJ1O2nRmz39dIr9m3hEDq1skYby5k1cJQdNWK4dMgvYcUEiA/9j7NcKfNZ5LGxn8w2LSLiigyH7pTAw==", + "requires": { + "binary-search-bounds": "^2.0.0", + "cubic-hermite": "^1.0.0" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "optional": true, "requires": { - "string-width": "^1.0.2 || 2" + "ms": "2.0.0" } }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "optional": true + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true } } }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "flatten-vertex-data": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz", + "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==", + "requires": { + "dtype": "^2.0.0" + } + }, + "flip-pixels": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz", + "integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA==" + }, + "flot-axislabels": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flot-axislabels/-/flot-axislabels-1.0.0.tgz", + "integrity": "sha512-Aiz5Za+nnZBjaTlhBcyBGc7uszgsNL7laSZ3jw++u8C4wn2QXg20sQlrUShARpNyvtdgdBDK7gIoIJbl8/HV3Q==" + }, + "flot-pie": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flot-pie/-/flot-pie-1.0.0.tgz", + "integrity": "sha512-wf/d6rYkyuSNS3AU7NANVY+tqEv5BkERwDB04zG8DsYeLukwR41Cr2DNmdz0Zrh5Q+Z34Q+atUH1iWKzy8Sv1A==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "font-atlas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz", + "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==", + "requires": { + "css-font": "^1.0.0" + } + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==" + }, + "font-measure": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz", + "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==", + "requires": { + "css-font": "^1.2.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "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==", + "dev": true, + "peer": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "gamma": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz", - "integrity": "sha1-MxVkNAO/J5BsqAqzfDbs6UQO8zA=" + "integrity": "sha512-IgHc/jnzNTA2KjXmRSx/CVd1ONp7HTAV81SLI+n3G6PyyHkakkE+2d3hteJYFm7aoe01NEl4m7ziUAsoWCc5AA==" + }, + "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==", + "dev": true, + "peer": true }, "geojson-vt": { "version": "3.2.1", @@ -4770,15 +24539,31 @@ "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" }, "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-canvas-context": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz", - "integrity": "sha1-1ue1C8TkyGNXzTnyJkeoS3NgHpM=" + "integrity": "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } }, "get-stream": { "version": "4.1.0", @@ -4789,22 +24574,19 @@ "pump": "^3.0.0" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "git-revision-webpack-plugin": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/git-revision-webpack-plugin/-/git-revision-webpack-plugin-3.0.4.tgz", - "integrity": "sha512-ym4Zkl32HtTRZVVgl1KoE+sWtgeFyDjN3vaBQfn8cCv1btAX7rdDY9tgpv4Zi+yxq150pp+pUkGH9L1lRpZOUg==", - "dev": true + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } }, "gl-axes3d": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/gl-axes3d/-/gl-axes3d-1.5.2.tgz", - "integrity": "sha512-47Cfh5KhUVRFtYXgufR4lGY5cyXH7SPgAlS1FlvTGK84spIYFCBMlOGUN3AdavGLGUOcXS4ml+tMM61cY6M3gg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/gl-axes3d/-/gl-axes3d-1.5.3.tgz", + "integrity": "sha512-KRYbguKQcDQ6PcB9g1pgqB8Ly4TY1DQODpPKiDTasyWJ8PxQk0t2Q7XoQQijNqvsguITCpVVCzNb5GVtIWiVlQ==", "requires": { "bit-twiddle": "^1.0.2", "dup": "^1.0.0", @@ -4824,7 +24606,7 @@ "gl-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/gl-buffer/-/gl-buffer-2.1.2.tgz", - "integrity": "sha1-LbjZwaVSf7oM25EonCBuiCuInNs=", + "integrity": "sha512-uVvLxxhEbQGl43xtDeKu75ApnrGyNHoPmOcvvuJNyP04HkK0/sX5Dll6OFffQiwSV4j0nlAZsgznvO3CPT3dFg==", "requires": { "ndarray": "^1.0.15", "ndarray-ops": "^1.1.0", @@ -4832,9 +24614,9 @@ } }, "gl-cone3d": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.5.1.tgz", - "integrity": "sha512-R8m2lPfVN5ip/IPzykvMNgUUGWTkp9rMuCrVknKIkhjH+gaQeGfwF3+WrB0kwq3FRWvlYWcfdvabv37sZ2rKYA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.5.2.tgz", + "integrity": "sha512-1JNeHH4sUtUmDA4ZK7Om8/kShwb8IZVAsnxaaB7IPRJsNGciLj1sTpODrJGeMl41RNkex5kXD2SQFrzyEAR2Rw==", "requires": { "colormap": "^2.3.1", "gl-buffer": "^2.1.2", @@ -4853,12 +24635,12 @@ "gl-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gl-constants/-/gl-constants-1.0.0.tgz", - "integrity": "sha1-WXpQTjZHUP9QJTqjX43qevSl0jM=" + "integrity": "sha512-3DNyoAUdb1c+o7jNk5Nm7eh6RSQFi9ZmMQIQb2xxsO27rUopE+IUhoh4xlUvZYBn1YPgUC8BlCnrVjXq/d2dQA==" }, "gl-contour2d": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/gl-contour2d/-/gl-contour2d-1.1.6.tgz", - "integrity": "sha512-n8nEFb4VRYooBo3+hbAgiXGELVn7PtYyVbj/hWmTNtrkxFK39Yr8LUczcT2uOOyzqq7sO3FH8+J8PSMFh+z+5A==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/gl-contour2d/-/gl-contour2d-1.1.7.tgz", + "integrity": "sha512-GdebvJ9DtT3pJDpoE+eU2q+Wo9S3MijPpPz5arZbhK85w2bARmpFpVfPaDlZqWkB644W3BlH8TVyvAo1KE4Bhw==", "requires": { "binary-search-bounds": "^2.0.4", "cdt2d": "^1.0.0", @@ -4872,9 +24654,9 @@ } }, "gl-error3d": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/gl-error3d/-/gl-error3d-1.0.15.tgz", - "integrity": "sha512-7mB1zU22Vzdvq0KzzYRzE0xvCRF9nHd1+9ElUqkvt0GMH0gVIpxKk+m3hNPM/iQHmNupcXaE1cBcOQE2agN3uA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/gl-error3d/-/gl-error3d-1.0.16.tgz", + "integrity": "sha512-TGJewnKSp7ZnqGgG3XCF9ldrDbxZrO+OWlx6oIet4OdOM//n8xJ5isArnIV/sdPJnFbhfoLxWrW9f5fxHFRQ1A==", "requires": { "gl-buffer": "^2.1.2", "gl-shader": "^4.2.1", @@ -4886,7 +24668,7 @@ "gl-fbo": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/gl-fbo/-/gl-fbo-2.0.5.tgz", - "integrity": "sha1-D6daSXz3h2lVMGkcjwSrtvtV+iI=", + "integrity": "sha512-tDq6zQSQzvvK2QwPV7ln7cf3rs0jV1rQXqKOEuB145LdN+xhADPBtXHDJ3Ftk80RAJimJU0AaQBgP/X6yYGNhQ==", "requires": { "gl-texture2d": "^2.0.0" } @@ -4894,7 +24676,7 @@ "gl-format-compiler-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/gl-format-compiler-error/-/gl-format-compiler-error-1.0.3.tgz", - "integrity": "sha1-DHmxdRiZzpcy6GJA8JCqQemEcag=", + "integrity": "sha512-FtQaBYlsM/rnz7YhLkxG9dLcNDB+ExErIsFV2DXl0nk+YgIZ2i0jMob4BrhT9dNa179zFb0gZMWpNAokytK+Ug==", "requires": { "add-line-numbers": "^1.0.1", "gl-constants": "^1.0.0", @@ -4903,22 +24685,22 @@ } }, "gl-heatmap2d": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/gl-heatmap2d/-/gl-heatmap2d-1.0.5.tgz", - "integrity": "sha512-nki9GIh0g4OXKNIrlnAT/gy/uXxkwrFKgI+XwRcUO6nLBM1WbI2hl8EPykNFXCqsyd08HJQbXKiqaHPW7cNpJg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gl-heatmap2d/-/gl-heatmap2d-1.1.1.tgz", + "integrity": "sha512-6Vo1fPIB1vQFWBA/MR6JAA16XuQuhwvZRbSjYEq++m4QV33iqjGS2HcVIRfJGX+fomd5eiz6bwkVZcKm69zQPw==", "requires": { - "binary-search-bounds": "^2.0.3", + "binary-search-bounds": "^2.0.4", "gl-buffer": "^2.1.2", - "gl-shader": "^4.0.5", + "gl-shader": "^4.2.1", "glslify": "^7.0.0", "iota-array": "^1.0.0", - "typedarray-pool": "^1.1.0" + "typedarray-pool": "^1.2.0" } }, "gl-line3d": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gl-line3d/-/gl-line3d-1.2.0.tgz", - "integrity": "sha512-du9GDF87DMfllND2pBjySyHhFaza9upw4t2GMoXn11/I38atO6+saiznuhKmfxuDnyxGdmmZF6/HPauk0owKDA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gl-line3d/-/gl-line3d-1.2.1.tgz", + "integrity": "sha512-eeb0+RI2ZBRqMYJK85SgsRiJK7c4aiOjcnirxv0830A3jmOc99snY3AbPcV8KvKmW0Yaf3KA4e+qNCbHiTOTnA==", "requires": { "binary-search-bounds": "^2.0.4", "gl-buffer": "^2.1.2", @@ -4930,15 +24712,10 @@ "ndarray": "^1.0.18" } }, - "gl-mat2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gl-mat2/-/gl-mat2-1.0.1.tgz", - "integrity": "sha1-FCUFcwpcL+Hp8l2ezj0NbMJxCjA=" - }, "gl-mat3": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gl-mat3/-/gl-mat3-1.0.0.tgz", - "integrity": "sha1-iWMyGcpCk3mha5GF2V1BcTRTuRI=" + "integrity": "sha512-obeEq9y7xaDoVkwMGJNL1upwpYlPJiXJFhREaNytMqUdfHKHNna9HvImmLV8F8Ys6QOYwPPddptZNoiiec/XOg==" }, "gl-mat4": { "version": "1.2.0", @@ -4946,83 +24723,73 @@ "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==" }, "gl-matrix": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.2.1.tgz", - "integrity": "sha512-YYVO8jUSf6+SakL4AJmx9Jc7zAZhkJQ+WhdtX3VQe5PJdCOX6/ybY4x1vk+h94ePnjRn6uml68+QxTAJneUpvA==" - }, - "gl-matrix-invert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gl-matrix-invert/-/gl-matrix-invert-1.0.0.tgz", - "integrity": "sha1-o2173jZUxFkKEn7nxo9uE/6oxj0=", - "requires": { - "gl-mat2": "^1.0.0", - "gl-mat3": "^1.0.0", - "gl-mat4": "^1.0.0" - } + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "gl-mesh3d": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.3.0.tgz", - "integrity": "sha512-iKx3v0xB/6Kej+GpMHhxzW6ziqiIjp6WOyAbuXvBRN9P5iIgzifgBYnDd1mYmCLWGmf85MCki/FvD223BOYFxg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.3.1.tgz", + "integrity": "sha512-pXECamyGgu4/9HeAQSE5OEUuLBGS1aq9V4BCsTcxsND4fNLaajEkYKUz/WY2QSYElqKdsMBVsldGiKRKwlybqA==", "requires": { "barycentric": "^1.0.1", "colormap": "^2.3.1", - "gl-buffer": "^2.0.8", - "gl-mat4": "^1.0.0", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", "gl-shader": "^4.2.1", - "gl-texture2d": "^2.0.8", - "gl-vao": "^1.1.3", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", "glsl-out-of-range": "^1.0.4", "glsl-specular-cook-torrance": "^2.0.1", "glslify": "^7.0.0", - "ndarray": "^1.0.15", - "normals": "^1.0.1", + "ndarray": "^1.0.18", + "normals": "^1.1.0", "polytope-closest-point": "^1.0.0", - "simplicial-complex-contour": "^1.0.0", + "simplicial-complex-contour": "^1.0.2", "typedarray-pool": "^1.1.0" } }, "gl-plot2d": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.4.3.tgz", - "integrity": "sha512-Ei6WC/SzQ7/qld8MMv7sWrFSdkq8/n7Xmdvj7sbwUkgsJirfknKfeq4DCwaMn9vD2rHOLmdT0NMW+HPrLKSeWQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.4.5.tgz", + "integrity": "sha512-6GmCN10SWtV+qHFQ1gjdnVubeHFVsm6P4zmo0HrPIl9TcdePCUHDlBKWAuE6XtFhiMKMj7R8rApOX8O8uXUYog==", "requires": { "binary-search-bounds": "^2.0.4", "gl-buffer": "^2.1.2", - "gl-select-static": "^2.0.5", + "gl-select-static": "^2.0.7", "gl-shader": "^4.2.1", "glsl-inverse": "^1.0.0", "glslify": "^7.0.0", - "text-cache": "^4.2.1" + "text-cache": "^4.2.2" } }, "gl-plot3d": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.4.4.tgz", - "integrity": "sha512-R/V4hSrE2sFD+Xls7D6qCOlWCRmqtUff0sKbeFJdI91HfFzPJPiy9Pqa/Jh2UsvdmwkkSQPNDcBvLd6TvhRC/g==", + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.4.7.tgz", + "integrity": "sha512-mLDVWrl4Dj0O0druWyHUK5l7cBQrRIJRn2oROEgrRuOgbbrLAzsREKefwMO0bA0YqkiZMFMnV5VvPA9j57X5Xg==", "requires": { "3d-view": "^2.0.0", "a-big-triangle": "^1.0.3", - "gl-axes3d": "^1.5.2", + "gl-axes3d": "^1.5.3", "gl-fbo": "^2.0.5", "gl-mat4": "^1.2.0", - "gl-select-static": "^2.0.4", + "gl-select-static": "^2.0.7", "gl-shader": "^4.2.1", - "gl-spikes3d": "^1.0.9", + "gl-spikes3d": "^1.0.10", "glslify": "^7.0.0", "has-passive-events": "^1.0.0", - "is-mobile": "^2.2.0", + "is-mobile": "^2.2.1", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", "mouse-wheel": "^1.2.0", - "ndarray": "^1.0.18", + "ndarray": "^1.0.19", "right-now": "^1.0.0" } }, "gl-pointcloud2d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/gl-pointcloud2d/-/gl-pointcloud2d-1.0.2.tgz", - "integrity": "sha512-KDfuJLg1dFWNPo6eJYgwUpNdVcIdK5y29ZiYpzzP0qh3eg0bSLMq8ZkaqvPmSJsFksUryT73IRunsuxJtTJkvA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gl-pointcloud2d/-/gl-pointcloud2d-1.0.3.tgz", + "integrity": "sha512-OS2e1irvJXVRpg/GziXj10xrFJm9kkRfFoB6BLUvkjCQV7ZRNNcs2CD+YSK1r0gvMwTg2T3lfLM3UPwNtz+4Xw==", "requires": { "gl-buffer": "^2.1.2", "gl-shader": "^4.2.1", @@ -5033,7 +24800,7 @@ "gl-quat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gl-quat/-/gl-quat-1.0.0.tgz", - "integrity": "sha1-CUXskjOG9FMpvl3DV7HIwtR1hsU=", + "integrity": "sha512-Pv9yvjJgQN85EbE79S+DF50ujxDkyjfYHIyXJcCRiimU1UxMY7vEHbVkj0IWLFaDndhfZT9vVOyfdMobLlrJsQ==", "requires": { "gl-mat3": "^1.0.0", "gl-vec3": "^1.0.3", @@ -5041,47 +24808,46 @@ } }, "gl-scatter3d": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.2.tgz", - "integrity": "sha512-oZh3WQ0bVXnpASpZmYmiEp7eUiD0oU6J4G5C9KUOhUo5d2gucvZEILAtfWmzCT3zsOltoROn4jGuuP2tlLN88Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.3.tgz", + "integrity": "sha512-nXqPlT1w5Qt51dTksj+DUqrZqwWAEWg0PocsKcoDnVNv0X8sGA+LBZ0Y+zrA+KNXUL0PPCX9WR9cF2uJAZl1Sw==", "requires": { - "gl-buffer": "^2.0.6", - "gl-mat4": "^1.0.0", - "gl-shader": "^4.2.0", - "gl-vao": "^1.1.2", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-vao": "^1.3.0", "glsl-out-of-range": "^1.0.4", "glslify": "^7.0.0", "is-string-blank": "^1.0.1", - "typedarray-pool": "^1.0.2", + "typedarray-pool": "^1.1.0", "vectorize-text": "^3.2.1" } }, "gl-select-box": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/gl-select-box/-/gl-select-box-1.0.3.tgz", - "integrity": "sha512-sQb18g1aZ6PJAsvsC8nNYhuhc2TYXNbzVbI0bP9AH9770NjrDnd7TC8HHcfu8nJXGPG69HjqR6EzS+QSqiXPSA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/gl-select-box/-/gl-select-box-1.0.4.tgz", + "integrity": "sha512-mKsCnglraSKyBbQiGq0Ila0WF+m6Tr+EWT2yfaMn/Sh9aMHq5Wt0F/l6Cf/Ed3CdERq5jHWAY5yxLviZteYu2w==", "requires": { "gl-buffer": "^2.1.2", - "gl-shader": "^4.0.5", + "gl-shader": "^4.2.1", "glslify": "^7.0.0" } }, "gl-select-static": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/gl-select-static/-/gl-select-static-2.0.5.tgz", - "integrity": "sha512-8H1M9ipHNsrVh8UjUmTv1xhhYjYzMnawAnw3n715Dh4DDoW32F3oBi80ev5qbJtQlvHrNkhHKuoMCJKBjfIt4g==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/gl-select-static/-/gl-select-static-2.0.7.tgz", + "integrity": "sha512-OvpYprd+ngl3liEatBTdXhSyNBjwvjMSvV2rN0KHpTU+BTi4viEETXNZXFgGXY37qARs0L28ybk3UQEW6C5Nnw==", "requires": { "bit-twiddle": "^1.0.2", - "cwise": "^1.0.3", - "gl-fbo": "^2.0.3", - "ndarray": "^1.0.15", + "gl-fbo": "^2.0.5", + "ndarray": "^1.0.18", "typedarray-pool": "^1.1.0" } }, "gl-shader": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gl-shader/-/gl-shader-4.2.1.tgz", - "integrity": "sha1-vJuAjpKTxRtmjojeYVsMETcI3C8=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gl-shader/-/gl-shader-4.3.1.tgz", + "integrity": "sha512-xLoN6XtRLlg97SEqtuzfKc+pVWpVkQ3YjDI1kuCale8tF7+zMhiKlMfmG4IMQPMdKJZQbIc/Ny8ZusEpfh5U+w==", "requires": { "gl-format-compiler-error": "^1.0.2", "weakmap-shim": "^1.1.0" @@ -5093,9 +24859,9 @@ "integrity": "sha512-QVeOZsi9nQuJJl7NB3132CCv5KA10BWxAY2QgJNsKqbLsG53B/TrGJpjIAohnJftdZ4fT6b3ZojWgeaXk8bOOA==" }, "gl-spikes3d": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/gl-spikes3d/-/gl-spikes3d-1.0.9.tgz", - "integrity": "sha512-laMxydgGdnE8kvd1YD9cNWrx0uSmrPj1Oi02cHhnxWIklut97w3F7mZKnmLMEyUkxpRLkEeQ7YkYy7Y+aUEblw==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/gl-spikes3d/-/gl-spikes3d-1.0.10.tgz", + "integrity": "sha512-lT3xroowOFxMvlhT5Mof76B2TE02l5zt/NIWljhczV2FFHgIVhA4jMrd5dIv1so1RXMBDJIKu0uJI3QKliDVLg==", "requires": { "gl-buffer": "^2.1.2", "gl-shader": "^4.2.1", @@ -5106,17 +24872,17 @@ "gl-state": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gl-state/-/gl-state-1.0.0.tgz", - "integrity": "sha1-Ji+qdYNbC5xTLBLzitxCXR0wzRc=", + "integrity": "sha512-Od836PpgCuTC0W7uHYnEEPRdQPL1FakWlznz3hRvlO6tD5sdLfBKX9qNRGy1DjfMCDTudhyYWxiWjhql1B8N4Q==", "requires": { "uniq": "^1.0.0" } }, "gl-streamtube3d": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.4.0.tgz", - "integrity": "sha512-WgRtdB77uFCN1lBZ6ogz7VTK4J8WwW5DGHvyB3LaBSZF3t5lf/KWeXPgm+xnNINlOy4JqJIgny+CtzwTHAk3Ew==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.4.1.tgz", + "integrity": "sha512-rH02v00kgwgdpkXVo7KsSoPp38bIAYR9TE1iONjcQ4cQAlDhrGRauqT/P5sUaOIzs17A2DxWGcXM+EpNQs9pUA==", "requires": { - "gl-cone3d": "^1.5.0", + "gl-cone3d": "^1.5.2", "gl-vec3": "^1.1.3", "gl-vec4": "^1.0.1", "glsl-inverse": "^1.0.0", @@ -5126,35 +24892,35 @@ } }, "gl-surface3d": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.4.6.tgz", - "integrity": "sha512-aItWQTNUX3JJc6i2FbXX82ljPZgDV3kXzkzANcBGoAnKwRpJw12WcMKKTL4sOCs9BW+3sx6BhR0P5+2zh5Scfw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.6.0.tgz", + "integrity": "sha512-x15+u4712ysnB85G55RLJEml6mOB4VaDn0VTlXCc9JcjRl5Es10Tk7lhGGyiPtkCfHwvhnkxzYA1/rHHYN7Y0A==", "requires": { "binary-search-bounds": "^2.0.4", "bit-twiddle": "^1.0.2", "colormap": "^2.3.1", "dup": "^1.0.0", - "gl-buffer": "^2.0.3", - "gl-mat4": "^1.0.0", - "gl-shader": "^4.2.0", - "gl-texture2d": "^2.0.0", - "gl-vao": "^1.1.1", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", + "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", "glsl-out-of-range": "^1.0.4", "glsl-specular-beckmann": "^1.1.2", "glslify": "^7.0.0", - "ndarray": "^1.0.16", + "ndarray": "^1.0.18", "ndarray-gradient": "^1.0.0", - "ndarray-ops": "^1.2.1", - "ndarray-pack": "^1.0.1", - "ndarray-scratch": "^1.1.1", + "ndarray-ops": "^1.2.2", + "ndarray-pack": "^1.2.1", + "ndarray-scratch": "^1.2.0", "surface-nets": "^1.0.2", - "typedarray-pool": "^1.0.0" + "typedarray-pool": "^1.1.0" } }, "gl-text": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.1.8.tgz", - "integrity": "sha512-whnq9DEFYbW92C4ONwk2eT0YkzmVPHoADnEtuzMOmit87XhgAhBrNs3lK9EgGjU/MoWYvlF6RkI8Kl7Yuo1hUw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.3.1.tgz", + "integrity": "sha512-/f5gcEMiZd+UTBJLTl3D+CkCB/0UFGTx3nflH8ZmyWcLkZhsZ1+Xx5YYkw2rgWAzgPeE35xCqBuHSoMKQVsR+w==", "requires": { "bit-twiddle": "^1.0.2", "color-normalize": "^1.5.0", @@ -5170,15 +24936,22 @@ "parse-rect": "^1.2.0", "parse-unit": "^1.0.1", "pick-by-alias": "^1.2.0", - "regl": "^1.3.11", + "regl": "^2.0.0", "to-px": "^1.0.1", "typedarray-pool": "^1.1.0" + }, + "dependencies": { + "regl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/regl/-/regl-2.1.0.tgz", + "integrity": "sha512-oWUce/aVoEvW5l2V0LK7O5KJMzUSKeiOwFuJehzpSFd43dO5spP9r+sSUfhKtsky4u6MCqWJaRL+abzExynfTg==" + } } }, "gl-texture2d": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/gl-texture2d/-/gl-texture2d-2.1.0.tgz", - "integrity": "sha1-/2gk5+fDGoum/c2+nlxpXX4hh8c=", + "integrity": "sha512-W0tzEjtlGSsCKq5FFwFVhH+fONFUTUeqM4HhA/BleygKaX39IwNTVOiqkwfu9szQZ4dQEq8ZDl7w1ud/eKLaZA==", "requires": { "ndarray": "^1.0.15", "ndarray-ops": "^1.2.2", @@ -5186,9 +24959,9 @@ } }, "gl-util": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.2.tgz", - "integrity": "sha512-8czWhGTGp/H4S35X1UxGbFlJ1hjtTFhm2mc85GcymEi1CDf633WJgtkCddEiSjIa4BnNxBrqOIhj6jlF6naPqw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.3.tgz", + "integrity": "sha512-dvRTggw5MSkJnCbh74jZzSoTOGnVYK+Bt+Ckqm39CVcl6+zSsxqWk4lr5NKhkqXHL6qvZAU9h17ZF8mIskY9mA==", "requires": { "is-browser": "^2.0.1", "is-firefox": "^1.0.3", @@ -5202,7 +24975,7 @@ "gl-vao": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/gl-vao/-/gl-vao-1.3.0.tgz", - "integrity": "sha1-6ekqqVWIyrnVwvBLaTRAw99pGSM=" + "integrity": "sha512-stSOZ+n0fnAxgDfipwKK/73AwzCNL+AFEc/v2Xm76nyFnUZGmQtD2FEC3lt1icoOHAzMgHBAjCue7dBIDeOTcw==" }, "gl-vec3": { "version": "1.1.3", @@ -5212,103 +24985,61 @@ "gl-vec4": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gl-vec4/-/gl-vec4-1.0.1.tgz", - "integrity": "sha1-l9loeCgbFLUyy84QF4Xf0cs0CWQ=" + "integrity": "sha512-/gx5zzIy75JXzke4yuwcbvK+COWf8UJbVCUPvhfsYVw1GVey4Eextk/0H0ctXnOICruNK7+GS4ILQzEQcHcPEg==" }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "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": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "is-glob": "^4.0.1" } }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - }, - "dependencies": { - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - } - } + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } + "peer": true }, "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" } }, "glsl-inject-defines": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz", - "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=", + "integrity": "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==", "requires": { "glsl-token-inject-block": "^1.0.0", "glsl-token-string": "^1.0.1", @@ -5318,7 +25049,7 @@ "glsl-inverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/glsl-inverse/-/glsl-inverse-1.0.0.tgz", - "integrity": "sha1-EsCx0GX1WERNHm/q95td34qRiuY=" + "integrity": "sha512-+BsseNlgqzd4IFX1dMqg+S0XuIXzH0acvTtW7svwhJESM1jb2BZFwdO+tOWdCXD5Zse6b9bOmzp5sCNA7GQ2QA==" }, "glsl-out-of-range": { "version": "1.0.4", @@ -5328,7 +25059,7 @@ "glsl-resolve": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz", - "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=", + "integrity": "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA==", "requires": { "resolve": "^0.6.1", "xtend": "^2.1.2" @@ -5337,19 +25068,19 @@ "resolve": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", - "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=" + "integrity": "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==" }, "xtend": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", - "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak=" + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==" } } }, "glsl-shader-name": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/glsl-shader-name/-/glsl-shader-name-1.0.0.tgz", - "integrity": "sha1-osMLO6c0mb77DMcYTXx3M91LSH0=", + "integrity": "sha512-OtHon0dPCbJD+IrVA1vw9QDlp2cS/f9z8X/0y+W7Qy1oZ3U1iFAQUEco2v30V0SAlVLDG5rEfhjEfc3DKdGbFQ==", "requires": { "atob-lite": "^1.0.0", "glsl-tokenizer": "^2.0.2" @@ -5358,12 +25089,12 @@ "glsl-specular-beckmann": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/glsl-specular-beckmann/-/glsl-specular-beckmann-1.1.2.tgz", - "integrity": "sha1-/OkFaTPs3yRWJ4N2pU0IKJPndfE=" + "integrity": "sha512-INvd7szO1twNPLGwE0Kf2xXIEy5wpOPl/LYoiw3+3nbAe6Rfn5rjdK9xvfnwoWksTCs3RejuLeAiZkLTkdFtwg==" }, "glsl-specular-cook-torrance": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/glsl-specular-cook-torrance/-/glsl-specular-cook-torrance-2.0.1.tgz", - "integrity": "sha1-qJHMBsjHtPRyhwK0gk/ay7ln148=", + "integrity": "sha512-bFtTfbgLXIbto/U6gM7h0IxoPMU+5zpMK5HoAaA2LnPuGk3JSzKAnsoyh5QGTT8ioIEQrjk6jcQNrgujPsP7rw==", "requires": { "glsl-specular-beckmann": "^1.1.1" } @@ -5371,12 +25102,12 @@ "glsl-token-assignments": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz", - "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8=" + "integrity": "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ==" }, "glsl-token-defines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz", - "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=", + "integrity": "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ==", "requires": { "glsl-tokenizer": "^2.0.0" } @@ -5384,12 +25115,12 @@ "glsl-token-depth": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz", - "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ=" + "integrity": "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg==" }, "glsl-token-descope": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz", - "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=", + "integrity": "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw==", "requires": { "glsl-token-assignments": "^2.0.0", "glsl-token-depth": "^1.1.0", @@ -5400,27 +25131,27 @@ "glsl-token-inject-block": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz", - "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ=" + "integrity": "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA==" }, "glsl-token-properties": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz", - "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4=" + "integrity": "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA==" }, "glsl-token-scope": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz", - "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E=" + "integrity": "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A==" }, "glsl-token-string": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz", - "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw=" + "integrity": "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg==" }, "glsl-token-whitespace-trim": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz", - "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA=" + "integrity": "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ==" }, "glsl-tokenizer": { "version": "2.1.5", @@ -5428,46 +25159,14 @@ "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==", "requires": { "through2": "^0.6.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } - } } }, "glslify": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.0.0.tgz", - "integrity": "sha512-yw8jDQIe9FlSH5NiZEqSAsCPj9HI7nhXgXLAgSv2Nm9eBPsFJmyN9+rNwbiozJapcj9xtc/71rMYlN9cxp1B8Q==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.1.1.tgz", + "integrity": "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog==", "requires": { - "bl": "^1.0.0", + "bl": "^2.2.1", "concat-stream": "^1.5.2", "duplexify": "^3.4.5", "falafel": "^2.1.0", @@ -5476,18 +25175,31 @@ "glsl-token-whitespace-trim": "^1.0.0", "glslify-bundle": "^5.0.0", "glslify-deps": "^1.2.5", - "minimist": "^1.2.0", + "minimist": "^1.2.5", "resolve": "^1.1.5", "stack-trace": "0.0.9", - "static-eval": "^2.0.0", + "static-eval": "^2.0.5", "through2": "^2.0.1", "xtend": "^4.0.0" }, "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } } } }, @@ -5509,51 +25221,49 @@ } }, "glslify-deps": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.1.tgz", - "integrity": "sha512-Ogm179MCazwIRyEqs3g3EOY4Y3XIAa0yl8J5RE9rJC6QH1w8weVOp2RZu0mvnYy/2xIas1w166YR2eZdDkWQxg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.2.tgz", + "integrity": "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag==", "requires": { "@choojs/findup": "^0.2.0", - "events": "^1.0.2", + "events": "^3.2.0", "glsl-resolve": "0.0.1", "glsl-tokenizer": "^2.0.0", "graceful-fs": "^4.1.2", "inherits": "^2.0.1", "map-limit": "0.0.1", "resolve": "^1.0.0" - }, - "dependencies": { - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - } } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "grid-index": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dev": true, "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "duplexer": "^0.1.2" } }, "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, "has": { @@ -5567,21 +25277,34 @@ "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + } } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-hover": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-hover/-/has-hover-1.0.1.tgz", - "integrity": "sha1-PZdDeusZnGK4rAisvcU9O8UsF/c=", + "integrity": "sha512-0G6w7LnlcpyDzpeGUTuT0CEw05+QlMuGVk1IHNAlHrGJITGodjZu3x8BNDUMfKJSZXNB2ZAclqc1bvrd+uUpfg==", "requires": { "is-browser": "^2.0.1" } @@ -5594,68 +25317,33 @@ "is-browser": "^2.0.1" } }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "has-value": { + "has-property-descriptors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-intrinsic": "^1.1.1" } }, - "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=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "has-symbols": "^1.0.2" } }, "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } + "peer": true }, "he": { "version": "1.2.0", @@ -5663,47 +25351,28 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "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==", - "dev": true - }, "highcharts": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-7.2.2.tgz", "integrity": "sha512-jRKujQuPKHLgGQd2sByBI9K5m56CInm2augVZnBYqdmyoU88hcI62uuAXHvxC3FW8YY0FJ74A1uw6sxrcmcmvg==" }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } + "peer": true }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "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 }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -5712,33 +25381,24 @@ "wbuf": "^1.1.0" } }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, "hsluv": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-0.0.3.tgz", - "integrity": "sha1-gpEH2vtKn4tSoYCe0C4JHq3mdUw=" + "integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==" }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } }, "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, "html-loader": { @@ -5769,128 +25429,117 @@ "uglify-js": "3.4.x" } }, - "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" }, "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" } } } }, + "html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==" + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", "dev": true, "requires": { - "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" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "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, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" } }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -5898,22 +25547,52 @@ "requires-port": "^1.0.0" } }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + } } }, - "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=", + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, "iconv-lite": { @@ -5926,42 +25605,21 @@ } }, "icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, - "requires": { - "postcss": "^7.0.14" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "image-palette": { @@ -5974,24 +25632,29 @@ "quantize": "^1.0.2" } }, + "image-size": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", + "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==" + }, "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "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": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" } }, "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" } }, "imports-loader": { @@ -6007,22 +25670,23 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "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=", + "integrity": "sha512-mKRJDXtzo1R9LxCuB1TdwZXHaPaIEldoGPsXy2jrJc/kufyqp8y/VAQQxThSxM2aroLoh6uObexPk1ASJ7FB7Q==", "requires": { "robust-orientation": "^1.1.2", "simplicial-complex": "^1.0.0" } }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true }, "infer-owner": { "version": "1.0.4", @@ -6033,7 +25697,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -6045,19 +25709,19 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" } }, "internmap": { @@ -6066,89 +25730,48 @@ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" }, "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, "interval-tree-1d": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.3.tgz", - "integrity": "sha1-j9veArayx9verWNry+2OCHENhcE=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz", + "integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==", "requires": { - "binary-search-bounds": "^1.0.0" - }, - "dependencies": { - "binary-search-bounds": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz", - "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k=" - } + "binary-search-bounds": "^2.0.0" } }, "invert-permutation": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-permutation/-/invert-permutation-1.0.0.tgz", - "integrity": "sha1-oKeAQurbNrwXVR54fv0UOa3VSTM=" + "integrity": "sha512-8f473/KSrnvyBd7Khr4PC5wPkAOehwkGc+AH5Q7D+U/fE+cdDob2FJ3naXAs4mspR9JIaEwbDI3me8H0KlVzSQ==" }, "iota-array": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", - "integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc=" - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "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=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, - "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=", - "dev": true, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-base64": { @@ -6156,13 +25779,21 @@ "resolved": "https://registry.npmjs.org/is-base64/-/is-base64-0.1.0.tgz", "integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg==" }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, "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=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, "is-blob": { @@ -6170,6 +25801,15 @@ "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz", "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==" }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", @@ -6181,86 +25821,54 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" - }, - "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=", - "dev": true, - "requires": { - "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" - } + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.6.tgz", + "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==" }, - "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=", - "dev": true, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "has": "^1.0.3" } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "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==", - "dev": true - } + "has-tostringtag": "^1.0.0" } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "dev": true }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, + "is-file-esm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-file-esm/-/is-file-esm-1.0.0.tgz", + "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==", + "dev": true, + "peer": true, + "requires": { + "read-pkg-up": "^7.0.1" + } + }, "is-finite": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", @@ -6269,7 +25877,7 @@ "is-firefox": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-firefox/-/is-firefox-1.0.3.tgz", - "integrity": "sha1-KioVZ3g6QX9uFYMjEI84YbCRhWI=" + "integrity": "sha512-6Q9ITjvWIm0Xdqv+5U12wgOKEM2KoBw4Y926m0OFkvlCxnbG94HKAsVz8w3fWcfAS5YA2fJORXX1dLrkprCCxA==" }, "is-float-array": { "version": "1.0.0", @@ -6277,18 +25885,23 @@ "integrity": "sha512-4ew1Sx6B6kEAl3T3NOM0yB94J3NZnBdNt4paw0e8nY73yHHTeTEhyQ3Lj7EQEnv5LD+GxNTaT4L46jcKjjpLiQ==" }, "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, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "requires": { - "number-is-nan": "^1.0.0" + "has-tostringtag": "^1.0.0" } }, "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==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -6297,67 +25910,47 @@ "is-iexplorer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-iexplorer/-/is-iexplorer-1.0.0.tgz", - "integrity": "sha1-HXK8ZtP+Iur2Fw3ajPEJQySM/HY=" + "integrity": "sha512-YeLzceuwg3K6O0MLM3UyUUjKAlyULetwryFp1mHy1I5PfArK0AEqlfa+MR4gkJjcbuJXoDJCvXbyqZVf5CR2Sg==" }, - "is-mobile": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.1.tgz", - "integrity": "sha512-6zELsfVFr326eq2CI53yvqq6YBanOxKBybwDT+MbMS2laBnK6Ez8m5XHSuTQQbnKRfpDzCod1CMWW5q3wZYMvA==" + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "is-mobile": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.2.tgz", + "integrity": "sha512-wW/SXnYJkTjs++tVK5b6kVITZpAZPtUrt9SF80vvxGiF/Oywal+COk1jlRkiVq15RFNEQKQY31TkV24/1T5cVg==" }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "requires": { - "is-path-inside": "^2.1.0" + "has-tostringtag": "^1.0.0" } }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" }, "is-plain-object": { "version": "2.0.4", @@ -6368,96 +25961,182 @@ "isobject": "^3.0.1" } }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "requires": { - "has": "^1.0.3" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-string-blank": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, "is-svg-path": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-svg-path/-/is-svg-path-1.0.2.tgz", - "integrity": "sha1-d6tZDBKz0gNI5cehPQBAyHeE3aA=" + "integrity": "sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg==" }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", "requires": { - "has-symbols": "^1.0.1" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" } }, - "is-windows": { + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-whitespace": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", + "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==", "dev": true }, "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, + "javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true, + "peer": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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 + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "joi": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", + "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "jquery": { "version": "1.12.4", "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz", - "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" + "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg==" }, "jquery-color": { - "version": "3.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/jquery-color/-/jquery-color-3.0.0-alpha.1.tgz", - "integrity": "sha512-UjsdBsWuehWLfnXfL4JZtxGbRB2eUz6Rhn3qxBKaVR3Qwm7fQlY1y/Va0o7GzDwGJnOvYKc3H+i2k7bC1yGcJg==" + "version": "3.0.0-alpha.2", + "resolved": "https://registry.npmjs.org/jquery-color/-/jquery-color-3.0.0-alpha.2.tgz", + "integrity": "sha512-dG5Y4YO/SQ7tgMYQjEu8zqFqx6gzrZyXhObGRIJkprBCaMsuH6vpxjXclK4d3qwkp1XZBlyOuf5Hv5Spjoy2aw==", + "requires": {} }, "jquery-flot-resize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jquery-flot-resize/-/jquery-flot-resize-1.0.0.tgz", - "integrity": "sha1-bR4TJl8p5MXV+bBhCjeKMDdyiMM=" + "integrity": "sha512-754cos9Lt6Ujx/MYgA0hDRvhmFzHfjhBj4Avjb7fRUfFA/1XfpK8Stc0qNMiGWfv/HmWieRDKP6STCqefDm94A==" }, "jquery-touchswipe": { "version": "1.6.19", @@ -6465,19 +26144,22 @@ "integrity": "sha512-b0BGje9reNRU3u6ksAK9QqnX7yBRgLNe/wYG7DOfyDlhBlYjayIT8bSOHmcuvptIDW/ubM9CTW/mnZf9Rohuow==" }, "jquery-ui": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", - "integrity": "sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE=" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.2.tgz", + "integrity": "sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==", + "requires": { + "jquery": ">=1.8.0 <4.0.0" + } }, "jquery-ui-timepicker-addon": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/jquery-ui-timepicker-addon/-/jquery-ui-timepicker-addon-1.6.3.tgz", - "integrity": "sha1-gDfDmwtjAoLdCzfditf8XhFjN38=" + "integrity": "sha512-JvZTp1vGN1Ws9tr39VPRBC1/cOVdY/pa6a2mMcAXpEgjrsXc02xBXQthDtQsBXEqR0FbZV+GdXi/c+LDpuyk/A==" }, "jquery.cookie": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha1-1j3OIJ6raR/mMxbbCMqeR+D5OFs=" + "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" }, "jquery.flot": { "version": "0.8.3", @@ -6487,12 +26169,72 @@ "jquery.flot.tooltip": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/jquery.flot.tooltip/-/jquery.flot.tooltip-0.9.0.tgz", - "integrity": "sha1-rha/lLJsLtmrTbFnu6Ut/bYVwd8=" + "integrity": "sha512-TV3umIfIPRq1UM0EUY4E/jimrh2j057J6SJB1HQnEeS64r8liec++F0WCNYsmcWDxz6Lo4E2lCA+SeOQS2W3bA==" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "js-beautify": { + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.6.tgz", + "integrity": "sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw==", + "dev": true, + "requires": { + "config-chain": "^1.1.13", + "editorconfig": "^0.15.3", + "glob": "^8.0.3", + "nopt": "^6.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "requires": { + "abbrev": "^1.0.0" + } + } + } }, - "js-base64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", - "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==", + "js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true }, "js-tokens": { @@ -6502,33 +26244,77 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-18.1.1.tgz", + "integrity": "sha512-NmJQbjQ/gpS/1at/ce3nCx89HbXL/f5OcenBe8wU1Eik0ROhyUc3LtmG3567dEHAGXkN8rmILW/qtCOPxPHQJw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" }, "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true } } }, + "jsdom-global": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", + "integrity": "sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==", + "dev": true, + "requires": {} + }, "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true }, "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==", - "dev": true + "dev": true, + "peer": true }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -6542,35 +26328,27 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true }, "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" } }, "kdbush": { @@ -6578,68 +26356,71 @@ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, "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==", - "dev": true + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } }, "klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", "dev": true }, - "last-call-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", "dev": true, "requires": { - "lodash": "^4.17.5", - "webpack-sources": "^1.1.0" + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" } }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + "launch-editor-middleware": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.6.0.tgz", + "integrity": "sha512-K2yxgljj5TdCeRN1lBtO3/J26+AIDDDw+04y6VAiZbWcTdBwsYN6RrZBnW5DN/QiSIdKNjKdATLUUluWWFYTIA==", + "dev": true, + "peer": true, + "requires": { + "launch-editor": "^2.6.0" + } }, "lerp": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/lerp/-/lerp-1.0.3.tgz", - "integrity": "sha1-oYyJaPkXiW3hXM/MKNVaa3Med24=" + "integrity": "sha512-70Rh4rCkJDvwWiTsyZ1HmJGvnyfFah4m6iTux29XmasRiZPDBpT9Cfa4ai73+uLZxnlKruUS62jj2lb11wURiA==" }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, "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=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { @@ -6651,119 +26432,276 @@ "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true, + "peer": true + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true, + "peer": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, - "loglevel": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", - "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", - "dev": true + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "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" + } + } + } }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", + "dev": true, + "peer": true, + "requires": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "peer": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "peer": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "peer": true + }, + "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==", + "dev": true, + "peer": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "peer": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "peer": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "peer": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "peer": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", + "dev": true, + "peer": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + } + } + } + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } }, "lower-case": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "dev": true }, "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "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, "requires": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" } }, "luxon": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.25.0.tgz", - "integrity": "sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ==" - }, - "magic-string": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.6.tgz", - "integrity": "sha512-3a5LOMSGoCTH5rbqobC2HuDNRtE2glHZ8J7pK+QZYppyWA36yuNpsX994rIY2nCuyP7CZYy7lQq/X2jygiZ89g==", - "requires": { - "sourcemap-codec": "^1.4.4" - } + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "p-defer": "^1.0.0" + "semver": "^6.0.0" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, "map-limit": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz", - "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=", + "integrity": "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg==", "requires": { "once": "~1.3.0" }, @@ -6771,56 +26709,47 @@ "once": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "integrity": "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w==", "requires": { "wrappy": "1" } } } }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, "mapbox-gl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.3.2.tgz", - "integrity": "sha512-6Ro7GbTMWxcbc836m6rbBNkesgTncbE1yXWeuHlr89esSqaItKr0+ntOu8rZie3fv+GtitkbODysXzIGCA7G+w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.10.1.tgz", + "integrity": "sha512-0aHt+lFUpYfvh0kMIqXqNXqoYMuhuAsMlw87TbhWrw78Tx2zfuPI0Lx31/YPUgJ+Ire0tzQ4JnuBL7acDNXmMg==", "requires": { - "@mapbox/geojson-rewind": "^0.4.0", + "@mapbox/geojson-rewind": "^0.5.0", "@mapbox/geojson-types": "^1.0.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^1.4.0", + "@mapbox/mapbox-gl-supported": "^1.5.0", "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^1.1.0", + "@mapbox/tiny-sdf": "^1.1.1", "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "csscolorparser": "~1.0.2", - "earcut": "^2.1.5", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.2", "geojson-vt": "^3.2.1", - "gl-matrix": "^3.0.0", + "gl-matrix": "^3.2.1", "grid-index": "^1.1.0", - "minimist": "0.0.8", + "minimist": "^1.2.5", "murmurhash-js": "^1.0.0", - "pbf": "^3.0.5", + "pbf": "^3.2.1", "potpack": "^1.0.1", "quickselect": "^2.0.0", "rw": "^1.3.3", - "supercluster": "^6.0.1", - "tinyqueue": "^2.0.0", + "supercluster": "^7.0.0", + "tinyqueue": "^2.0.3", "vt-pbf": "^3.1.1" } }, "marching-simplex-table": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/marching-simplex-table/-/marching-simplex-table-1.0.0.tgz", - "integrity": "sha1-vBYlbg+Pm1WKqbKHL4gy2UM/Uuo=", + "integrity": "sha512-PexXXVF4f5Bux3vGCNlRRBqF/GyTerNo77PbBz8g/MFFXv212b48IGVglj/VfaYBRY6vlFQffa9dFbCCN0+7LA==", "requires": { "convex-hull": "^1.0.3" } @@ -6828,7 +26757,7 @@ "markdown": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz", - "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=", + "integrity": "sha512-ctGPIcuqsYoJ493sCtFK7H4UEgMWAUdXeBhPbdsg1W0LsV9yJELAHRsMmWfTgao6nH0/x5gf9FmsbxiXnrgaIQ==", "requires": { "nopt": "~2.1.1" } @@ -6836,7 +26765,7 @@ "mat4-decompose": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mat4-decompose/-/mat4-decompose-1.0.4.tgz", - "integrity": "sha1-ZetP451wh496RE60Yk1S9+frL68=", + "integrity": "sha512-M3x6GXrzRTt5Ok4/bcHFc869Pe8F3uWaSp3xkUpi+uaTRulPXIZ1GWD13Z3A8WK2bxTrcvX21mjp05gUy/Dwbw==", "requires": { "gl-mat4": "^1.0.1", "gl-vec3": "^1.0.2" @@ -6845,7 +26774,7 @@ "mat4-interpolate": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mat4-interpolate/-/mat4-interpolate-1.0.4.tgz", - "integrity": "sha1-Vf/p6zw1KV4sDVqfdyXZBoqJ/3Q=", + "integrity": "sha512-+ulnoc6GUHq8eGZGbLyhQU61tx2oeNAFilV/xzCCzLV+F3nDk8jqERUqRmx8eNMMMvrdvoRSw0JXmnisfVPY9A==", "requires": { "gl-mat4": "^1.0.1", "gl-vec3": "^1.0.2", @@ -6857,7 +26786,7 @@ "mat4-recompose": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mat4-recompose/-/mat4-recompose-1.0.4.tgz", - "integrity": "sha1-OVPCMP8kc9x3LuAUpSySXPgbDk0=", + "integrity": "sha512-s1P2Yl4LQxq8dN0CgJE+mCO8y3IX/SmauSZ+H0zJsE1UKlgJ9loInfPC/OUxn2MzUW9bfBZf0Wcc2QKA3/e6FQ==", "requires": { "gl-mat4": "^1.0.1" } @@ -6865,64 +26794,44 @@ "math-log2": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", - "integrity": "sha1-+4lBvl9evol55xjmJzsXjlhpRWU=" + "integrity": "sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA==" }, "matrix-camera-controller": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.3.tgz", - "integrity": "sha1-NeUmDMHNVQliunmfLY1OlLGjk3A=", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.4.tgz", + "integrity": "sha512-zsPGPONclrKSImNpqqKDTcqFpWLAIwMXEJtCde4IFPOw1dA9udzFg4HOFytOTosOFanchrx7+Hqq6glLATIxBA==", "requires": { - "binary-search-bounds": "^1.0.0", + "binary-search-bounds": "^2.0.0", "gl-mat4": "^1.1.2", "gl-vec3": "^1.0.3", "mat4-interpolate": "^1.0.3" - }, - "dependencies": { - "binary-search-bounds": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz", - "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k=" - } - } - }, - "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==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" } }, "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", "dev": true, "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" + "fs-monkey": "^1.0.3" } }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", "dev": true, "requires": { "errno": "^0.1.3", @@ -6932,7 +26841,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "dev": true }, "merge-source-map": { @@ -6944,6 +26853,12 @@ "source-map": "^0.6.1" } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6953,59 +26868,38 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "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" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true }, "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "requires": { - "mime-db": "1.43.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -7015,15 +26909,53 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", - "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } } }, "minimalistic-assert": { @@ -7032,77 +26964,411 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, - "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=", - "dev": true - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "yallist": "^4.0.0" } }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mocha": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { - "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==", + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "isexe": "^2.0.0" } } } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "mochapack": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mochapack/-/mochapack-2.1.4.tgz", + "integrity": "sha512-qoZYT8ibht6z35e936P6Y/3nynFX843Jm+2l/pNWB3Sue63IHBfqZeAnF6Ypez85kUll7HtapMogfO2hGb2N2Q==", "dev": true, "requires": { - "minimist": "0.0.8" + "@babel/runtime-corejs2": "^7.0.0", + "chalk": "^2.4.2", + "chokidar": "^3.5.2", + "glob-parent": "5.1.2", + "globby": "^10.0.1", + "interpret": "^1.2.0", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.15", + "memory-fs": "^0.4.1", + "minimatch": "^3.0.4", + "nodent-runtime": "^3.2.1", + "normalize-path": "^3.0.0", + "progress": "^2.0.3", + "source-map-support": "^0.5.13", + "toposort": "^2.0.2", + "yargs": "14.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "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" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.0.0.tgz", + "integrity": "sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, + "module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==", + "dev": true, + "peer": true + }, "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=", + "integrity": "sha512-ixQ3qdXTVHvR7eAoOjKY8kGxl9YjOFtzi7qOjwmFFPfBqZHVOjUFOBy/Dk9dusamRSPJe9ggyfSypRbs0Bl8BA==", "requires": { "robust-orientation": "^1.1.3" } @@ -7110,7 +27376,7 @@ "mouse-change": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz", - "integrity": "sha1-wrd+W/o0pDzhRFyBV6Tk3JiVwU8=", + "integrity": "sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ==", "requires": { "mouse-event": "^1.0.0" } @@ -7118,70 +27384,49 @@ "mouse-event": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz", - "integrity": "sha1-s3ie23EJmX1aky0dAdqhVDpQFzI=" + "integrity": "sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw==" }, "mouse-event-offset": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mouse-event-offset/-/mouse-event-offset-3.0.2.tgz", - "integrity": "sha1-39hqbiSMa6jK1TuQXVA3ogY+mYQ=" + "integrity": "sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w==" }, "mouse-wheel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz", - "integrity": "sha1-bSkDseqPtI5h8bU7kDZ3PwQs21w=", + "integrity": "sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw==", "requires": { "right-now": "^1.0.0", "signum": "^1.0.0", "to-px": "^1.0.1" - }, - "dependencies": { - "signum": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", - "integrity": "sha1-dKfSvyogtA66FqkrFSEk8dVZ+nc=" - } } }, - "move-concurrently": { + "mrmime": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "requires": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" } }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, "mumath": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/mumath/-/mumath-3.3.4.tgz", - "integrity": "sha1-SNSg8P2MrU57Mglu6JsWGmPTC78=", + "integrity": "sha512-VAFIOG6rsxoc7q/IaY3jdjmrsuX9f15KlRLYTHmixASBZkZEKC1IFqE2BC5CdhXmK6WLM1Re33z//AGmeRI6FA==", "requires": { "almost-equal": "^1.1.0" } @@ -7189,39 +27434,31 @@ "murmurhash-js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", - "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=" + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, - "optional": true + "peer": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } }, "nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==", + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "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" - } + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "ndarray": { "version": "1.0.19", @@ -7235,46 +27472,29 @@ "ndarray-extract-contour": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ndarray-extract-contour/-/ndarray-extract-contour-1.0.1.tgz", - "integrity": "sha1-Cu4ROjozsia5DEiIz4d79HUTBeQ=", + "integrity": "sha512-iDngNoFRqrqbXGLP8BzyGrybw/Jnkkn7jphzc3ZFfO7dfmpL1Ph74/6xCi3xSvJFyVW90XpMnd766jTaRPsTCg==", "requires": { "typedarray-pool": "^1.0.0" } }, - "ndarray-fill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ndarray-fill/-/ndarray-fill-1.0.2.tgz", - "integrity": "sha1-owpg9xiODJWC/N1YiWrNy1IqHtY=", - "requires": { - "cwise": "^1.0.10" - } - }, "ndarray-gradient": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ndarray-gradient/-/ndarray-gradient-1.0.0.tgz", - "integrity": "sha1-t0kaUVxqZJ8ZpiMk//byf8jCU5M=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ndarray-gradient/-/ndarray-gradient-1.0.1.tgz", + "integrity": "sha512-+xONVi7xxTCGL6KOb11Yyoe0tPNqAUKF39CvFoRjL5pdOmPd2G2pckK9lD5bpLF3q45LLnYNyiUSJSdNmQ2MTg==", "requires": { "cwise-compiler": "^1.0.0", "dup": "^1.0.0" } }, - "ndarray-homography": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ndarray-homography/-/ndarray-homography-1.0.0.tgz", - "integrity": "sha1-w1UW6oa8KGK06ASiNqJwcwn+KWs=", - "requires": { - "gl-matrix-invert": "^1.0.0", - "ndarray-warp": "^1.0.0" - } - }, "ndarray-linear-interpolate": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ndarray-linear-interpolate/-/ndarray-linear-interpolate-1.0.0.tgz", - "integrity": "sha1-eLySuFuavBW25n7mWCj54hN65ys=" + "integrity": "sha512-UN0f4+6XWsQzJ2pP5gVp+kKn5tJed6mA3K/L50uO619+7LKrjcSNdcerhpqxYaSkbxNJuEN76N05yBBJySnZDw==" }, "ndarray-ops": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", - "integrity": "sha1-WeiNLDKn7ryxvGkPrhQVeVV6YU4=", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", "requires": { "cwise-compiler": "^1.0.0" } @@ -7282,7 +27502,7 @@ "ndarray-pack": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", - "integrity": "sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo=", + "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", "requires": { "cwise-compiler": "^1.1.2", "ndarray": "^1.0.13" @@ -7291,7 +27511,7 @@ "ndarray-scratch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ndarray-scratch/-/ndarray-scratch-1.2.0.tgz", - "integrity": "sha1-YwRjbWLrqT20cnrBPGkzQdulDgE=", + "integrity": "sha512-a4pASwB1jQyJcKLYrwrladVfDZDUGc78qLJZbHyb1Q4rhte0URhzc6ALQpBcauwgov0sXLwZz3vYH5jKAhSMIg==", "requires": { "ndarray": "^1.0.14", "ndarray-ops": "^1.2.1", @@ -7301,41 +27521,32 @@ "ndarray-sort": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ndarray-sort/-/ndarray-sort-1.0.1.tgz", - "integrity": "sha1-/qBbTLg0x/TgIWo1TzynUTAN/Wo=", + "integrity": "sha512-Gpyis5NvEPOQVadDOG+Dx8bhYCkaxn5IlA4Ig/jBJIlnW1caDiPneQLzT/+AIMeHEmqlGZfdqO/I1TXJS2neAw==", "requires": { "typedarray-pool": "^1.0.0" } }, - "ndarray-warp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ndarray-warp/-/ndarray-warp-1.0.1.tgz", - "integrity": "sha1-qKElqqu6C+v5O9bKg+ar1oIqNOA=", - "requires": { - "cwise": "^1.0.4", - "ndarray-linear-interpolate": "^1.0.0" - } - }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, "nextafter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz", - "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=", + "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==", "requires": { "double-bits": "^1.1.0" } @@ -7356,83 +27567,91 @@ } }, "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" - } - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true - }, - "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==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } + "lodash": "^4.17.21" } }, - "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "requires": { - "semver": "^6.3.0" + "whatwg-url": "^5.0.0" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "nodent-runtime": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/nodent-runtime/-/nodent-runtime-3.2.1.tgz", + "integrity": "sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==", + "dev": true + }, "nopt": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", - "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=", + "integrity": "sha512-x8vXm7BZ2jE1Txrxh/hO74HTuYZQEbo8edoRcANgdZ4+PCV+pbjd/xdummkmjjC7LU5EjPzlu8zEq/oxWylnKA==", "requires": { "abbrev": "1" } }, + "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", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7442,24 +27661,18 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, "normalize-svg-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", - "integrity": "sha1-RWNg5g7Odfvve11+FgSA5//Rb+U=" + "integrity": "sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA==" }, "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true }, "normalize.css": { "version": "8.0.1", @@ -7469,155 +27682,79 @@ "normals": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz", - "integrity": "sha1-MltZXtNK/kZ6bFWhT9kIV4f/WcA=" + "integrity": "sha512-XWeliW48BLvbVJ+cjQAOE+tA0m1M7Yi1iTPphAS9tBmW1A/c/cOVnEUecPCCMH5lEAihAcG6IRle56ls9k3xug==" }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "requires": { "path-key": "^2.0.0" } }, "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==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" } }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==" }, "number-is-integer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-integer/-/number-is-integer-1.0.1.tgz", - "integrity": "sha1-5ZvKFy/+0nMY55x862y3LAlbIVI=", + "integrity": "sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg==", "requires": { "is-finite": "^1.0.1" } }, - "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 - }, "numeric": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz", - "integrity": "sha1-dlsCvvl5iPz4gNTrPza4D6MTNao=" + "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw==" + }, + "nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "dev": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "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.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "object-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", - "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==" + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" } }, "obuf": { @@ -7627,9 +27764,9 @@ "dev": true }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "requires": { "ee-first": "1.1.1" @@ -7644,36 +27781,37 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } }, - "opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "mimic-fn": "^2.1.0" } }, - "optimize-css-assets-webpack-plugin": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", - "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==", + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "requires": { - "cssnano": "^4.1.10", - "last-call-webpack-plugin": "^3.0.0" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -7687,79 +27825,134 @@ "word-wrap": "~1.2.3" } }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "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" + } + } + } + }, "orbit-camera-controller": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/orbit-camera-controller/-/orbit-camera-controller-4.0.0.tgz", - "integrity": "sha1-bis28OeHhmPDMPUNqbfOaGwncAU=", + "integrity": "sha512-/XTmpr6FUT6MuKPBGN2nv9cS8jhhVs8do71VagBQS5p4rxM04MhqSnI/Uu+gVNN5s6KPcS73o1dHzjuDThEJUA==", "requires": { "filtered-vector": "^1.2.1", "gl-mat4": "^1.0.3" } }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true }, "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } }, "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dev": true, "requires": { - "retry": "^0.12.0" + "@types/retry": "0.12.0", + "retry": "^0.13.1" } }, "p-try": { @@ -7771,37 +27964,20 @@ "pad-left": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz", - "integrity": "sha1-GeVzXqmDlaJs7carkm6tEPMQDUw=", + "integrity": "sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==", "requires": { "repeat-string": "^1.3.0" } }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, "papaparse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz", - "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==" - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.2.tgz", + "integrity": "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==" }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", "dev": true, "requires": { "no-case": "^2.2.0" @@ -7814,41 +27990,25 @@ "dev": true, "requires": { "callsites": "^3.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - } } }, "parenthesis": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.7.tgz", - "integrity": "sha512-iMtu+HCbLXVrpf6Ys/4YKhcFxbux3xK4ZVB9r+a2kMSqeeQWQoDNYlXIsOjwlT2ldYXZ3k5PVeBnYn7fbAo/Bg==" + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz", + "integrity": "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==" }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "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": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "@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" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, "parse-rect": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz", @@ -7860,12 +28020,38 @@ "parse-svg-path": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", - "integrity": "sha1-en7A0esG+lMlx9PgCbhZoJtdSes=" + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==" }, "parse-unit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz", - "integrity": "sha1-fhu21b7zh0wo45JSaiVBFwKR7s8=" + "integrity": "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg==" + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "peer": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "peer": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "peer": true + } + } }, "parseurl": { "version": "1.3.3", @@ -7873,74 +28059,76 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "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==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + } + } }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "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=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", "dev": true }, "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true }, "pbf": { "version": "3.2.1", @@ -7951,28 +28139,15 @@ "resolve-protobuf-schema": "^2.1.0" } }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "permutation-parity": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/permutation-parity/-/permutation-parity-1.0.0.tgz", - "integrity": "sha1-AXTVH8pwSxG5pLFSsj1Tf9xrXvQ=", + "integrity": "sha512-mRaEvnnWolbZuErWD08StRUZP9YOWG3cURP5nYpRg1D2PENzPXCUrPv8/bOk0tfln0hISLZjOdOcQCbsVpL2nQ==", "requires": { "typedarray-pool": "^1.0.0" } @@ -7980,7 +28155,7 @@ "permutation-rank": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz", - "integrity": "sha1-n9mLvOzwj79ZlLXq3JSmLmeUg7U=", + "integrity": "sha512-kmXwlQcd4JlV8g61jz0xDyroFNlJ/mP+KbSBllMuQD7FvaQInRnnAStElcppkUXd8qVFLvemy6msUmBn7sDzHg==", "requires": { "invert-permutation": "^1.0.0", "typedarray-pool": "^1.0.0" @@ -7989,66 +28164,90 @@ "pick-by-alias": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz", - "integrity": "sha1-X3yysfIabh6ISgyHhVqko3NhEHs=" + "integrity": "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==" }, "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, - "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, - "requires": { - "pinkie": "^2.0.0" - } - }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } } }, "planar-dual": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/planar-dual/-/planar-dual-1.0.2.tgz", - "integrity": "sha1-tqQjVSOxsMt55fkm+OozXdmC1WM=", + "integrity": "sha512-jfQCbX1kXu53+enC+BPQlfoZI1u5m8IUhFVtFG+9tUj84wnuaYNheR69avYWCNXWnUCkwUajmYMqX9M2Ruh4ug==", "requires": { "compare-angle": "^1.0.0", "dup": "^1.0.0" } }, "planar-graph-to-polyline": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/planar-graph-to-polyline/-/planar-graph-to-polyline-1.0.5.tgz", - "integrity": "sha1-iCuGBRmbqIv9RkyVUzA1VsUrmIo=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/planar-graph-to-polyline/-/planar-graph-to-polyline-1.0.6.tgz", + "integrity": "sha512-h8a9kdAjo7mRhC0X6HZ42xzFp7vKDZA+Hygyhsq/08Qi4vVAQYJaLLYLvKUUzRbVKvdYqq0reXHyV0EygyEBHA==", "requires": { "edges-to-adjacency-list": "^1.0.0", "planar-dual": "^1.0.0", - "point-in-big-polygon": "^2.0.0", + "point-in-big-polygon": "^2.0.1", "robust-orientation": "^1.0.1", "robust-sum": "^1.0.0", "two-product": "^1.0.0", @@ -8056,128 +28255,128 @@ } }, "plotly.js": { - "version": "1.52.2", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.52.2.tgz", - "integrity": "sha512-EZHr2ekxVNjucmNhku+JiLFoLV1wH6gW15T/6SFqtHFMvJZY4HeZogtRUPTLijXBPfusMWxxIKEDTzlMOLXajQ==", + "version": "1.58.5", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.58.5.tgz", + "integrity": "sha512-ChTlnFXB4tB0CzcG1mqgUKYnrJsZ8REDGox8BHAa/ltsd48MOAhOmFgjyDxwsXyjjgwOI296GeYDft8g4ftLHQ==", "requires": { "@plotly/d3-sankey": "0.7.2", "@plotly/d3-sankey-circular": "0.33.1", + "@plotly/point-cluster": "^3.1.9", "@turf/area": "^6.0.1", "@turf/bbox": "^6.0.1", "@turf/centroid": "^6.0.2", "alpha-shape": "^1.0.0", "canvas-fit": "^1.5.0", - "color-normalize": "^1.5.0", - "color-rgba": "^2.1.1", + "color-alpha": "1.0.4", + "color-normalize": "1.5.0", + "color-parse": "1.3.8", + "color-rgba": "2.1.1", "convex-hull": "^1.0.3", "country-regex": "^1.1.0", - "d3": "^3.5.12", - "d3-force": "^1.0.6", + "d3": "^3.5.17", + "d3-force": "^1.2.1", "d3-hierarchy": "^1.1.9", "d3-interpolate": "^1.4.0", + "d3-time-format": "^2.2.3", "delaunay-triangulate": "^1.1.6", - "es6-promise": "^3.0.2", - "fast-isnumeric": "^1.1.3", - "gl-cone3d": "^1.5.1", - "gl-contour2d": "^1.1.6", - "gl-error3d": "^1.0.15", - "gl-heatmap2d": "^1.0.5", - "gl-line3d": "^1.1.11", + "es6-promise": "^4.2.8", + "fast-isnumeric": "^1.1.4", + "gl-cone3d": "^1.5.2", + "gl-contour2d": "^1.1.7", + "gl-error3d": "^1.0.16", + "gl-heatmap2d": "^1.1.0", + "gl-line3d": "1.2.1", "gl-mat4": "^1.2.0", - "gl-mesh3d": "^2.3.0", - "gl-plot2d": "^1.4.3", - "gl-plot3d": "^2.4.2", - "gl-pointcloud2d": "^1.0.2", - "gl-scatter3d": "^1.2.2", - "gl-select-box": "^1.0.3", + "gl-mesh3d": "^2.3.1", + "gl-plot2d": "^1.4.5", + "gl-plot3d": "^2.4.7", + "gl-pointcloud2d": "^1.0.3", + "gl-scatter3d": "^1.2.3", + "gl-select-box": "^1.0.4", "gl-spikes2d": "^1.0.2", - "gl-streamtube3d": "^1.4.0", - "gl-surface3d": "^1.4.6", + "gl-streamtube3d": "^1.4.1", + "gl-surface3d": "^1.6.0", "gl-text": "^1.1.8", - "glslify": "^7.0.0", + "glslify": "^7.1.1", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", - "is-mobile": "^2.1.0", - "mapbox-gl": "1.3.2", + "image-size": "^0.7.5", + "is-mobile": "^2.2.2", + "mapbox-gl": "1.10.1", "matrix-camera-controller": "^2.1.3", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", "mouse-wheel": "^1.2.0", - "ndarray": "^1.0.18", - "ndarray-fill": "^1.0.2", - "ndarray-homography": "^1.0.0", - "point-cluster": "^3.1.8", + "ndarray": "^1.0.19", + "ndarray-linear-interpolate": "^1.0.0", + "parse-svg-path": "^0.1.2", "polybooljs": "^1.2.0", - "regl": "^1.3.11", - "regl-error2d": "^2.0.8", - "regl-line2d": "^3.0.15", - "regl-scatter2d": "^3.1.7", - "regl-splom": "^1.0.8", + "regl": "^1.6.1", + "regl-error2d": "^2.0.11", + "regl-line2d": "^3.0.18", + "regl-scatter2d": "^3.2.1", + "regl-splom": "^1.0.12", "right-now": "^1.0.0", "robust-orientation": "^1.1.3", "sane-topojson": "^4.0.0", "strongly-connected-components": "^1.0.1", "superscript-text": "^1.0.0", "svg-path-sdf": "^1.1.3", - "tinycolor2": "^1.4.1", - "topojson-client": "^2.1.0", + "tinycolor2": "^1.4.2", + "to-px": "1.0.1", + "topojson-client": "^3.1.0", "webgl-context": "^2.2.0", "world-calendars": "^1.0.3" - } - }, - "point-cluster": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/point-cluster/-/point-cluster-3.1.8.tgz", - "integrity": "sha512-7klIr45dpMeZuqjIK9+qBg3m2IhyZJNJkdqjJFw0Olq75FM8ojrTMjClVUrMjNYRVqtwztxCHH71Fyjhg+YwyQ==", - "requires": { - "array-bounds": "^1.0.1", - "array-normalize": "^1.1.4", - "binary-search-bounds": "^2.0.4", - "bubleify": "^1.1.0", - "clamp": "^1.0.1", - "defined": "^1.0.0", - "dtype": "^2.0.0", - "flatten-vertex-data": "^1.0.2", - "is-obj": "^1.0.1", - "math-log2": "^1.0.1", - "parse-rect": "^1.2.0", - "pick-by-alias": "^1.2.0" }, "dependencies": { - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "requires": { + "d3-time": "1" + } } } }, "point-in-big-polygon": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/point-in-big-polygon/-/point-in-big-polygon-2.0.0.tgz", - "integrity": "sha1-ObYT6mzxfWtD4Yj3fzTETGszulU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/point-in-big-polygon/-/point-in-big-polygon-2.0.1.tgz", + "integrity": "sha512-DtrN8pa2VfMlvmWlCcypTFeBE4+OYz1ojDNJLKCWa4doiVAD6PRBbxFYAT71tsp5oKaRXT5sxEiHCAQKb1zr2Q==", "requires": { - "binary-search-bounds": "^1.0.0", + "binary-search-bounds": "^2.0.0", "interval-tree-1d": "^1.0.1", "robust-orientation": "^1.1.3", "slab-decomposition": "^1.0.1" - }, - "dependencies": { - "binary-search-bounds": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz", - "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k=" - } } }, "polybooljs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/polybooljs/-/polybooljs-1.2.0.tgz", - "integrity": "sha1-tDkMLgedTCYtOyUExiiNlbp6R1g=" + "integrity": "sha512-mKjR5nolISvF+q2BtC1fi/llpxBPTQ3wLWN8+ldzdw2Hocpc8C72ZqnamCM4Z6z+68GVVjkeM01WJegQmZ8MEQ==" }, "polytope-closest-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/polytope-closest-point/-/polytope-closest-point-1.0.0.tgz", - "integrity": "sha1-5uV/QIGrXox3i4Ee8G4sSK4zjD8=", + "integrity": "sha512-rvmt1e2ci9AUyWeHg+jsNuhGC4eBtxX4WjD9uDdvQzv2I1CVJSgbblJTslNXpGUu4KZSsUtSzvIdHKRKfRF3kw==", "requires": { "numeric": "^1.2.6" } @@ -8185,75 +28384,68 @@ "portal-vue": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.7.tgz", - "integrity": "sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==" + "integrity": "sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g==", + "requires": {} }, "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, + "peer": true, "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "requires": { "ms": "^2.1.1" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "peer": true, + "requires": { + "minimist": "^1.2.6" + } } } }, - "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=", - "dev": true - }, "postcss": { - "version": "8.2.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.10.tgz", - "integrity": "sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==", - "dev": true, + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + } } }, "postcss-calc": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", - "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "dev": true, "requires": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" } }, "postcss-color-function": { @@ -8284,166 +28476,58 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "postcss-extend-rule": { "version": "3.0.0", @@ -8456,15 +28540,20 @@ "postcss-tape": "^5.0.2" }, "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" } } } @@ -8472,7 +28561,7 @@ "postcss-functions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", - "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", + "integrity": "sha512-N5yWXWKA+uhpLQ9ZhBRl2bIAdM6oVJYpDojuI1nF2SzXBimJcdjFwiAouBVbO5VuOF3qA6BSFWFc3wXbbj72XQ==", "requires": { "glob": "^7.1.2", "object-assign": "^4.1.1", @@ -8494,14 +28583,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -8517,29 +28598,19 @@ } }, "postcss-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", - "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dev": true, "requires": { "camelcase-css": "^2.0.1", - "postcss": "^7.0.18" - }, - "dependencies": { - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - } + "postcss": "^8.1.6" } }, "postcss-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.2.0.tgz", - "integrity": "sha512-mqgScxHqbiz1yxbnNcPdKYo/6aVt+XExURmEbQlviFVWogDbM4AJ0A/B+ZBpYsJrTRxKw7HyRazg9x0Q9SWwLA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", "dev": true, "requires": { "cosmiconfig": "^7.0.0", @@ -8549,60 +28620,10 @@ "semver": "^7.3.4" }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@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" - } - }, - "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" - } - }, - "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==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -8610,275 +28631,83 @@ "json5": "^2.1.2" } }, - "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, - "requires": { - "yallist": "^4.0.0" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "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", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "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 - }, - "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 - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, "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==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", "dev": true, "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" } }, "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==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", "dev": true, "requires": { - "browserslist": "^4.0.0", + "browserslist": "^4.16.6", "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" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" } }, "postcss-message-helpers": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "integrity": "sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==", "dev": true }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", "dev": true, "requires": { - "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" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", "dev": true, "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } + "postcss-selector-parser": "^6.0.5" } }, "postcss-mixins": { @@ -8891,181 +28720,51 @@ "postcss-js": "^3.0.3", "postcss-simple-vars": "^6.0.3", "sugarss": "^3.0.3" - }, - "dependencies": { - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "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 - }, - "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==", - "dev": true, - "requires": { - "camelcase-css": "^2.0.1", - "postcss": "^8.1.6" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } } }, "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "dev": true, - "requires": { - "postcss": "^7.0.5" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "postcss-modules-local-by-default": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", - "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", - "dev": true, - "requires": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.16", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" } }, "postcss-modules-scope": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz", - "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", "dev": true, "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "postcss-selector-parser": "^6.0.4" } }, "postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, "requires": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "icss-utils": "^5.0.0" } }, "postcss-nested": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz", - "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.4" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", - "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1", - "util-deprecate": "^1.0.2" - } - } + "postcss-selector-parser": "^6.0.6" } }, "postcss-nesting": { @@ -9077,373 +28776,132 @@ "postcss": "^7.0.2" }, "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" } } } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } - } + "requires": {} }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", "dev": true, "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", "dev": true, "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "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==", - "dev": true - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" } }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0" } }, "postcss-scss": { @@ -9456,40 +28914,46 @@ } }, "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==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "requires": { "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "util-deprecate": "^1.0.2" } }, "postcss-simple-vars": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-6.0.3.tgz", "integrity": "sha512-fkNn4Zio8vN4vIig9IFdb8lVlxWnYR769RgvxCM6YWlFKie/nQaOcaMMMFz/s4gsfHW4/5bJW+i57zD67mQU7g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-strip-inline-comments": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/postcss-strip-inline-comments/-/postcss-strip-inline-comments-0.1.5.tgz", - "integrity": "sha1-f/a83BTmM+1M36AguuPt2tT4S5A=", + "integrity": "sha512-4EW5hYyv2syFyIBahkXGhZppp9zb5wD5NJ2R65WjXnB5q8T0g4VyLBTevU6ZpxtaN4HkoYZhV03DGUf5Ptd4FA==", "dev": true, "requires": { "postcss": "^5.0.18" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -9502,7 +28966,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } @@ -9510,7 +28974,7 @@ "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true }, "postcss": { @@ -9528,13 +28992,22 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "requires": { "has-flag": "^1.0.0" @@ -9543,34 +29016,13 @@ } }, "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", "dev": true, "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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==", - "dev": true - } + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" } }, "postcss-tape": { @@ -9580,27 +29032,12 @@ "dev": true }, "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==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", "dev": true, "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } + "postcss-selector-parser": "^6.0.5" } }, "postcss-utilities": { @@ -9613,61 +29050,71 @@ "postcss-value-parser": "^4.0.0" }, "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" } } } }, "postcss-value-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", - "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "potpack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.1.tgz", - "integrity": "sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "optional": true + }, + "pretty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", + "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==", + "dev": true, + "requires": { + "condense-newlines": "^0.2.1", + "extend-shallow": "^2.0.1", + "js-beautify": "^1.6.12" + } }, "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "lodash": "^4.17.20", + "renderkid": "^3.0.0" } }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==" }, "private": { "version": "0.1.8", @@ -9675,21 +29122,33 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, "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==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "progress-webpack-plugin": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz", + "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^2.1.0", + "figures": "^2.0.0", + "log-update": "^2.3.0" + } + }, "promise": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.3.tgz", - "integrity": "sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz", + "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==", "requires": { "asap": "~2.0.6" } @@ -9697,49 +29156,55 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, "protocol-buffers-schema": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz", - "integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA==" + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } } }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "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" - } + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true }, "pump": { "version": "3.0.0", @@ -9751,29 +29216,6 @@ "once": "^1.3.1" } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -9805,6 +29247,14 @@ "source-map": "^0.6.1", "supports-color": "^6.1.0" } + }, + "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" + } } } }, @@ -9822,57 +29272,38 @@ }, "dependencies": { "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" } } }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "quantize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/quantize/-/quantize-1.0.2.tgz", - "integrity": "sha1-0lrCAKd7bXD0ASfKFxoQ4zyFRt4=" + "integrity": "sha512-25P7wI2UoDbIQsQp50ARkt+5pwPsOq7G/BqvT5xAbapnRoNWMN8/p55H9TXd5MuENiJnm5XICB2H2aDZGwts7w==" }, "quat-slerp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/quat-slerp/-/quat-slerp-1.0.1.tgz", - "integrity": "sha1-K6oVzjprvcMkHZcusXKDE57Wnyk=", + "integrity": "sha512-OTozCDeP5sW7cloGR+aIycctZasBhblk1xdsSGP1Iz5pEwDqyChloTmc96xsDfusFD7GRxwDDu+tpJX0Wa1kJw==", "requires": { "gl-quat": "^1.0.0" } }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, "queue-microtask": { @@ -9886,60 +29317,6 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, - "quote-stream": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", - "integrity": "sha1-zeKelMQJsW4Z3HCYuJtmWPlyHTs=", - "requires": { - "minimist": "0.0.8", - "through2": "~0.4.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - } - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "requires": { - "object-keys": "~0.4.0" - } - } - } - }, "raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -9957,16 +29334,6 @@ "safe-buffer": "^5.1.0" } }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -9976,47 +29343,126 @@ "rat-vec": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz", - "integrity": "sha1-Dd4rZrezS7G80qI4BerIBth/0X8=", + "integrity": "sha512-FbxGwkQxmw4Jx41LR7yMOR+g8M9TWCEmf/SUBQVLuK2eh0nThnffF7IUualr3XE2x5F8AdLiCVeSGwXd4snfgg==", "requires": { "big-rat": "^1.0.3" } }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dev": true, "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "raw-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz", - "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^1.0.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } } }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, "requires": { "pify": "^2.3.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "peer": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" }, "dependencies": { - "pify": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "peer": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "peer": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "peer": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "peer": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "peer": true } } }, @@ -10032,23 +29478,28 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + } } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" } }, "recast": { "version": "0.11.23", "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", - "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "integrity": "sha512-+nixG+3NugceyR8O1bLU45qs84JgI3+8EauyRZafLgC9XbdAOIVgwV1Pe2da0YzGo62KzWoZwUpVEQf6qNAXWA==", "dev": true, "requires": { "ast-types": "0.9.6", @@ -10057,27 +29508,27 @@ "source-map": "~0.5.0" }, "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg==", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true } } }, - "redeyed": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz", - "integrity": "sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8=", + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, "requires": { - "esprima": "~1.0.4" - }, - "dependencies": { - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" - } + "resolve": "^1.9.0" } }, "reduce-css-calc": { @@ -10099,104 +29550,67 @@ "reduce-simplicial-complex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/reduce-simplicial-complex/-/reduce-simplicial-complex-1.0.0.tgz", - "integrity": "sha1-dNaWovg196bc2SBl/YxRgfLt+Lw=", + "integrity": "sha512-t+nT7sHDtcxBx8TbglqfLsLKoFiSn9hp6GFojJEThHBAFv72wQeq/uRiPYZa4Xb8FR1Ye1foRcBV3Ki6bgm+pQ==", "requires": { "cell-orientation": "^1.0.1", "compare-cell": "^1.0.0", "compare-oriented-cell": "^1.0.1" } }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "requires": { - "regenerate": "^1.4.0" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "regex-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regex-regex/-/regex-regex-1.0.0.tgz", - "integrity": "sha1-kEih6uuHD01IDavHb8Qs3MC8OnI=" - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" + "integrity": "sha512-FPbEhFTLpxKNgHKay3zMfkHzFK2ebViAlyvsz5euO4kwekH0T6fAL4Sdo2CgQ7Y1tGB5HqQm8SBq7pW5GegvVA==" }, - "regjsparser": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz", - "integrity": "sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA==", + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "requires": { - "jsesc": "~0.5.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "regl": { - "version": "1.3.13", - "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.13.tgz", - "integrity": "sha512-TTiCabJbbUykCL4otjqOvKqDFJhvJOT7xB51JxcDeSHGrEJl1zz4RthPcoOogqfuR3ECN4Te790DfHCXzli5WQ==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/regl/-/regl-1.7.0.tgz", + "integrity": "sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w==" }, "regl-error2d": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.8.tgz", - "integrity": "sha512-5nszdicXbimRUnYB42i+O7KPcla7PzI62nZLCP6qVRKlQCf3rSrWbikMNd1S84LE8+deWHWcb8rZ/v7rZ9qmmw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.12.tgz", + "integrity": "sha512-r7BUprZoPO9AbyqM5qlJesrSRkl+hZnVKWKsVp7YhOl/3RIpi4UDGASGJY0puQ96u5fBYw/OlqV24IGcgJ0McA==", "requires": { "array-bounds": "^1.0.1", - "bubleify": "^1.2.0", "color-normalize": "^1.5.0", "flatten-vertex-data": "^1.0.2", "object-assign": "^4.1.1", "pick-by-alias": "^1.2.0", - "to-float32": "^1.0.1", + "to-float32": "^1.1.0", "update-diff": "^1.1.0" } }, "regl-line2d": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.15.tgz", - "integrity": "sha512-RuQbg9iZ6MyuInG8izF6zjQ/2g4qL6sg1egiuFalWzaGSvuve/IWBsIcqKTlwpiEsRt9b4cHu9NYs2fLt1gYJw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.1.2.tgz", + "integrity": "sha512-nmT7WWS/WxmXAQMkgaMKWXaVmwJ65KCrjbqHGOUjjqQi6shfT96YbBOvelXwO9hG7/hjvbzjtQ2UO0L3e7YaXQ==", "requires": { "array-bounds": "^1.0.1", + "array-find-index": "^1.0.2", "array-normalize": "^1.1.4", - "bubleify": "^1.2.0", "color-normalize": "^1.5.0", "earcut": "^2.1.5", "es6-weak-map": "^2.0.3", @@ -10205,19 +29619,20 @@ "object-assign": "^4.1.1", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", - "to-float32": "^1.0.1" + "to-float32": "^1.1.0" } }, "regl-scatter2d": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.1.7.tgz", - "integrity": "sha512-FWw1hMsQrV3Y0zMU8YOytGjwSBuV3V58t8GR/mhlSL2S04jXLK1m2eAa/rDP3SpvMDkdVEr744PPDeHwsZVUhA==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.2.8.tgz", + "integrity": "sha512-bqrqJyeHkGBa9mEfuBnRd7FUtdtZ1l+gsM2C5Ugr1U3vJG5K3mdWdVWtOAllZ5FHHyWJV/vgjVvftgFUg6CDig==", "requires": { + "@plotly/point-cluster": "^3.1.9", "array-range": "^1.0.1", "array-rearrange": "^2.2.2", "clamp": "^1.0.1", "color-id": "^1.1.0", - "color-normalize": "1.5.0", + "color-normalize": "^1.5.0", "color-rgba": "^2.1.1", "flatten-vertex-data": "^1.0.2", "glslify": "^7.0.0", @@ -10226,128 +29641,104 @@ "object-assign": "^4.1.1", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", - "point-cluster": "^3.1.8", - "to-float32": "^1.0.1", + "to-float32": "^1.1.0", "update-diff": "^1.1.0" } }, "regl-splom": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.8.tgz", - "integrity": "sha512-4GQTgcArCbGLsXhgalWVBxeW7OXllnu+Gvil/4SbQQmtiqLCl+xgF79pISKY9mLXTlobxiX7cVKdjGjp25559A==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.14.tgz", + "integrity": "sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw==", "requires": { "array-bounds": "^1.0.1", "array-range": "^1.0.1", - "bubleify": "^1.2.0", "color-alpha": "^1.0.4", - "defined": "^1.0.0", "flatten-vertex-data": "^1.0.2", - "left-pad": "^1.3.0", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", - "point-cluster": "^3.1.8", "raf": "^3.4.1", - "regl-scatter2d": "^3.1.2" + "regl-scatter2d": "^3.2.3" } }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "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=", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, "requires": { - "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" } }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "requires": { - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "resolve-from": "^5.0.0" }, "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } + "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 } } }, "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "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 }, "resolve-protobuf-schema": { @@ -10358,30 +29749,20 @@ "protocol-buffers-schema": "^3.3.1" } }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "resumer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", - "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "requires": { - "through": "~2.3.4" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true }, "reusify": { @@ -10393,62 +29774,32 @@ "rgb": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/rgb/-/rgb-0.1.0.tgz", - "integrity": "sha1-vieykej+/+rBvZlylyG/pA/AN7U=", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "integrity": "sha512-F49dXX73a92N09uQkfCp2QjwXpmJcn9/i9PvjmwsSIXUGqRLCf/yx5Q9gRxuLQTq248kakqQuc8GX/U/CxSqlA==", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } - }, "right-now": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz", - "integrity": "sha1-bolgne69fc2vja7Mmuo5z1haCRg=" + "integrity": "sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg==" }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "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" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, "robust-compress": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-compress/-/robust-compress-1.0.0.tgz", - "integrity": "sha1-TPYsSzGNgwhRYBK7jBF1Lzkymxs=" + "integrity": "sha512-E8btSpQ6zZr7LvRLrLvb+N5rwQ0etUbsXFKv5NQj6TVK6RYT00Qg9iVFvIWR+GxXUvpes7FDN0WfXa3l7wtGOw==" }, "robust-determinant": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/robust-determinant/-/robust-determinant-1.1.0.tgz", - "integrity": "sha1-jsrnm3nKqz509t6+IjflORon6cc=", + "integrity": "sha512-xva9bx/vyAv3pVYL2++vlnvM9q7oQOeCS5iscmlWtmaXHEgI4GFWeuYPUVVhvmYwx9N49EsQTonVJihYtcMo1Q==", "requires": { "robust-compress": "^1.0.0", "robust-scale": "^1.0.0", @@ -10459,16 +29810,16 @@ "robust-dot-product": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-dot-product/-/robust-dot-product-1.0.0.tgz", - "integrity": "sha1-yboBeL0sMEv9cl9Y6Inx2UYARVM=", + "integrity": "sha512-Nu/wah8B8RotyZLRPdlEL0ZDh3b7wSwUBLdbTHwS/yw0qqjMJ943PSCkd6EsF5R5QFDWF2x77DGsbmnv9/7/ew==", "requires": { "robust-sum": "^1.0.0", "two-product": "^1.0.0" } }, "robust-in-sphere": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/robust-in-sphere/-/robust-in-sphere-1.1.3.tgz", - "integrity": "sha1-HFiD0WpOkjkpR27zSBmFe/Kpz3U=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/robust-in-sphere/-/robust-in-sphere-1.2.1.tgz", + "integrity": "sha512-3zJdcMIOP1gdwux93MKTS0RiMYEGwQBoE5R1IW/9ZQmGeZzP7f7i4+xdcK8ujJvF/dEOS1WPuI9IB1WNFbj3Cg==", "requires": { "robust-scale": "^1.0.0", "robust-subtract": "^1.0.0", @@ -10479,15 +29830,15 @@ "robust-linear-solve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-linear-solve/-/robust-linear-solve-1.0.0.tgz", - "integrity": "sha1-DNasUEBpGm8qo81jEdcokFyjofE=", + "integrity": "sha512-I1qW8Bl9+UYeGNh2Vt8cwkcD74xWMyjnU6lSVcZrf0eyfwPmreflY3v0SvqCZOj5ddxnSS1Xp31igbFNcg1TGQ==", "requires": { "robust-determinant": "^1.1.0" } }, "robust-orientation": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz", - "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.2.1.tgz", + "integrity": "sha512-FuTptgKwY6iNuU15nrIJDLjXzCChWB+T4AvksRtwPS/WZ3HuP1CElCm1t+OBfgQKfWbtZIawip+61k7+buRKAg==", "requires": { "robust-scale": "^1.0.2", "robust-subtract": "^1.0.0", @@ -10498,7 +29849,7 @@ "robust-product": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-product/-/robust-product-1.0.0.tgz", - "integrity": "sha1-aFJQAHzbunzx3nW/9tKScBEJir4=", + "integrity": "sha512-7ww6m+ICW6Dt7ylHVy1aeeNwTfMXfh2BHqHVNE+CHvrU9sI97Vb6uHnid0MN3I9afTI5DXOB7q4SQa2fxuo2Gw==", "requires": { "robust-scale": "^1.0.0", "robust-sum": "^1.0.0" @@ -10507,7 +29858,7 @@ "robust-scale": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz", - "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=", + "integrity": "sha512-jBR91a/vomMAzazwpsPTPeuTPPmWBacwA+WYGNKcRGSh6xweuQ2ZbjRZ4v792/bZOhRKXRiQH0F48AvuajY0tQ==", "requires": { "two-product": "^1.0.2", "two-sum": "^1.0.0" @@ -10516,7 +29867,7 @@ "robust-segment-intersect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/robust-segment-intersect/-/robust-segment-intersect-1.0.1.tgz", - "integrity": "sha1-MlK2oPwboUreaRXMvgnLzpqrHBw=", + "integrity": "sha512-QWngxcL7rCRLK7nTMcTNBPi/q+fecrOo6aOtTPnXjT/Dve5AK20DzUSq2fznUS+rCAxyir6OdPgDCzcUxFtJoQ==", "requires": { "robust-orientation": "^1.1.3" } @@ -10524,12 +29875,12 @@ "robust-subtract": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz", - "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo=" + "integrity": "sha512-xhKUno+Rl+trmxAIVwjQMiVdpF5llxytozXJOdoT4eTIqmqsndQqFb1A0oiW3sZGlhMRhOi6pAD4MF1YYW6o/A==" }, "robust-sum": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz", - "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k=" + "integrity": "sha512-AvLExwpaqUqD1uwLU6MwzzfRdaI6VEZsyvQ3IAQ0ZJ08v1H+DTyqskrf2ZJyh0BDduFVLN7H04Zmc+qTiahhAw==" }, "run-parallel": { "version": "1.2.0", @@ -10540,34 +29891,16 @@ "queue-microtask": "^1.2.2" } }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, "rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, "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==" }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10579,89 +29912,106 @@ "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-4.0.0.tgz", "integrity": "sha512-bJILrpBboQfabG3BNnHI2hZl52pbt80BE09u4WhnrmzuF2JbMKZdl62G5glXskJ46p+gxE2IzOwGj/awR4g8AA==" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } }, "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", "dev": true, "requires": { - "node-forge": "0.9.0" + "node-forge": "^1" } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } }, "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==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } }, "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -10673,10 +30023,25 @@ "parseurl": "~1.3.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "requires": { "depd": "~1.1.2", @@ -10688,7 +30053,13 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "setprototypeof": { @@ -10696,98 +30067,65 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true } } }, "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==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "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 - }, - "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==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "kind-of": "^6.0.2" + }, + "dependencies": { + "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==", + "dev": true + } } }, "shallow-copy": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", - "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" - }, - "sharkdown": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/sharkdown/-/sharkdown-0.1.1.tgz", - "integrity": "sha512-exwooSpmo5s45lrexgz6Q0rFQM574wYIX3iDZ7RLLqOb7IAoQZu9nxlZODU972g19sR69OIpKP2cpHTzU+PHIg==", - "requires": { - "cardinal": "~0.4.2", - "minimist": "0.0.5", - "split": "~0.2.10" - }, - "dependencies": { - "minimist": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", - "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" - } - } + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -10796,24 +30134,46 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", "dev": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "signum": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz", - "integrity": "sha1-q1UbEAM1EHCnBHg/GgnF52kfnPY=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz", + "integrity": "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==" }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" }, @@ -10828,7 +30188,7 @@ "simplicial-complex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz", - "integrity": "sha1-bDOk7Wn81Nkbe8rdOzC2NoPq4kE=", + "integrity": "sha512-mHauIKSOy3GquM5VnYEiu7eP5y4A8BiaN9ezUUgyYFz1k68PqDYcyaH3kenp2cyvWZE96QKE3nrxYw65Allqiw==", "requires": { "bit-twiddle": "^1.0.0", "union-find": "^1.0.0" @@ -10837,7 +30197,7 @@ "simplicial-complex-boundary": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simplicial-complex-boundary/-/simplicial-complex-boundary-1.0.1.tgz", - "integrity": "sha1-csn/HiTeqjdMm7L6DL8MCB6++BU=", + "integrity": "sha512-hz/AaVbs+s08EVoxlbCE68AlC6/mxFJLxJrGRMbDoTjz3030nhcOq+w5+f0/ZaU2EYjmwa8CdVKpiRVIrhaZjA==", "requires": { "boundary-cells": "^2.0.0", "reduce-simplicial-complex": "^1.0.0" @@ -10846,7 +30206,7 @@ "simplicial-complex-contour": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/simplicial-complex-contour/-/simplicial-complex-contour-1.0.2.tgz", - "integrity": "sha1-iQqsrChDZTQBEFRc8mKaJuBL+dE=", + "integrity": "sha512-Janyqvpa7jgr9MJbwR/XGyYz7bdhXNq7zgHxD0G54LCRNyn4bf3Hely2iWQeK/IGu3c5BaWFUh7ElxqXhKrq0g==", "requires": { "marching-simplex-table": "^1.0.0", "ndarray": "^1.0.15", @@ -10857,7 +30217,7 @@ "simplify-planar-graph": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/simplify-planar-graph/-/simplify-planar-graph-2.0.1.tgz", - "integrity": "sha1-vIWJNyXzLo+oriVoE5hEbSy892Y=", + "integrity": "sha512-KdC2ZPFvrGl9+lH/P3Yik7G0si2Zpk6Xiqjq8l9U1lOox5a/9dGLjevi9tvqoh4V7yQbs7fs6+rNCOAdrzUktw==", "requires": { "robust-orientation": "^1.0.1", "simplicial-complex": "^0.3.3" @@ -10866,12 +30226,12 @@ "bit-twiddle": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-0.0.2.tgz", - "integrity": "sha1-wurruVKjuUrMFASX4c3NLxoz9Y4=" + "integrity": "sha512-76iFAOrkcuw5UPA30Pt32XaytMHXz/04JembgIwsQAp7ImHYSWNq1shBbrlWf6CUvh1+amQ81LI8hNhqQgsBEw==" }, "simplicial-complex": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-0.3.3.tgz", - "integrity": "sha1-TDDK1X+eRXKd2PMGyHU1efRr6Z4=", + "integrity": "sha512-JFSxp7I5yORuKSuwGN96thhkqZVvYB4pkTMkk+PKP2QsOYYU1e84OBoHwOpFyFmjyvB9B3UDZKzHQI5S/CPUPA==", "requires": { "bit-twiddle": "~0.0.1", "union-find": "~0.0.3" @@ -10880,203 +30240,83 @@ "union-find": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/union-find/-/union-find-0.0.4.tgz", - "integrity": "sha1-uFSzMBYZva0USwAUx4+W6sDS8PY=" - } - } - }, - "slab-decomposition": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.2.tgz", - "integrity": "sha1-He1WdU1AixBznxRRA9/GGAf2UTQ=", - "requires": { - "binary-search-bounds": "^1.0.0", - "functional-red-black-tree": "^1.0.0", - "robust-orientation": "^1.1.3" - }, - "dependencies": { - "binary-search-bounds": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz", - "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k=" - } - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "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==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "integrity": "sha512-207oken6EyGDCBK5l/LTPsWfgy8N8s6idwRK2TG0ssWhzPlxEDdBA8nIV+eLbkEMdA8pAwE8F7/xwv2sCESVjQ==" } } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" } }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, + "slab-decomposition": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.3.tgz", + "integrity": "sha512-1EfR304JHvX9vYQkUi4AKqN62mLsjk6W45xTk/TxwN8zd3HGwS7PVj9zj0I6fgCZqfGlimDEY+RzzASHn97ZmQ==", "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "binary-search-bounds": "^2.0.0", + "functional-red-black-tree": "^1.0.0", + "robust-orientation": "^1.1.3" } }, - "sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "ms": "^2.1.1" + "color-convert": "^2.0.1" } }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "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": { - "websocket-driver": ">=0.5.1" + "color-name": "~1.1.4" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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 } } }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "requires": { - "is-plain-obj": "^1.0.0" + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" } }, "source-list-map": { @@ -11090,44 +30330,57 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, - "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==", - "dev": true, - "requires": { - "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" - } + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "spdx-correct": { + "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" + } + }, + "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 }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "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" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true }, "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, "requires": { "debug": "^4.1.0", @@ -11135,23 +30388,6 @@ "http-deceiver": "^1.2.7", "select-hose": "^2.0.0", "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "spdy-transport": { @@ -11168,21 +30404,6 @@ "wbuf": "^1.7.3" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -11196,44 +30417,27 @@ } } }, - "split": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", - "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", - "requires": { - "through": "2" - } - }, "split-polygon": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split-polygon/-/split-polygon-1.0.0.tgz", - "integrity": "sha1-DqzIoTanaxKj2VJW6n2kXbDC0kc=", + "integrity": "sha512-nBFcgQUVEE8dcOjuKaRdlM53k8RxUYpRxZ//n0pHJQGhbVscrsti+gllJI3pK3y7fgFwGWgt7NFhAX5sz0UoWQ==", "requires": { "robust-dot-product": "^1.0.0", "robust-sum": "^1.0.0" } }, - "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==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "minipass": "^3.1.1" } }, "stable": { @@ -11245,219 +30449,60 @@ "stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" - }, - "static-eval": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.3.tgz", - "integrity": "sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w==", - "requires": { - "escodegen": "^1.11.1" - } + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==" }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } + "peer": true }, - "static-module": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.5.0.tgz", - "integrity": "sha1-J9qYg8QajNCSNvhC8MHrxu32PYY=", + "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==", "requires": { - "concat-stream": "~1.6.0", - "duplexer2": "~0.0.2", - "escodegen": "~1.3.2", - "falafel": "^2.1.0", - "has": "^1.0.0", - "object-inspect": "~0.4.0", - "quote-stream": "~0.0.0", - "readable-stream": "~1.0.27-1", - "shallow-copy": "~0.0.1", - "static-eval": "~0.2.0", - "through2": "~0.4.1" + "escodegen": "^1.11.1" }, "dependencies": { "escodegen": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", - "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "requires": { - "esprima": "~1.1.1", - "estraverse": "~1.5.0", - "esutils": "~1.0.0", - "source-map": "~0.1.33" + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" } }, - "esprima": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", - "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=" - }, "estraverse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", - "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" - }, - "esutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", - "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "object-inspect": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", - "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w=" - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, - "static-eval": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-0.2.4.tgz", - "integrity": "sha1-t9NNg4k3uWn5ZBygfUj47eJj6ns=", - "requires": { - "escodegen": "~0.0.24" - }, - "dependencies": { - "escodegen": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz", - "integrity": "sha1-Dk/xcV8yh3XWyrUaxEpAbNer/9M=", - "requires": { - "esprima": "~1.0.2", - "estraverse": "~1.3.0", - "source-map": ">= 0.1.2" - } - }, - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" - }, - "estraverse": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", - "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI=" - } - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "through2": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - } - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "requires": { - "object-keys": "~0.4.0" - } + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" } } }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true }, - "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==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "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==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true + "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" + } }, "string-split-by": { "version": "1.0.0", @@ -11479,110 +30524,81 @@ "atob-lite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" + "integrity": "sha512-LEeSAWeh2Gfa2FtlQE1shxQ8zi5F9GHarrGKz08TMdODD5T4eH6BMsvtnhbWZ+XQn+Gb6om/917ucvRu7l7ukw==" } } }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.trim": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz", - "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, - "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==", + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "requires": { - "safe-buffer": "~5.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" } }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "strongly-connected-components": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz", - "integrity": "sha1-CSDitN9nyOrulsa2I0/inoc9upk=" + "integrity": "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA==" }, "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==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dev": true, - "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", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" } }, "sugarss": { @@ -11595,9 +30611,9 @@ } }, "supercluster": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-6.0.2.tgz", - "integrity": "sha512-aa0v2HURjBTOpbcknilcfxGDuArM8khklKSmZ/T8ZXL0BuRwb5aRw95lz+2bmWpFvCXDX/+FzqHxmg0TIaJErw==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", "requires": { "kdbush": "^3.0.0" } @@ -11605,20 +30621,25 @@ "superscript-text": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz", - "integrity": "sha1-58snUlZzYN9QvrBhDOjfPXHY39g=" + "integrity": "sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==" }, "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==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { "has-flag": "^3.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "surface-nets": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz", - "integrity": "sha1-5DPIy7qUpydMb0yZVStGG/H8eks=", + "integrity": "sha512-Se+BaCb5yc8AV1IfT6TwTWEe/KuzzjzcMQQCbcIahzk9xRO5bIxxGM2MmKxE9nmq8+RD8DLBLXu0BjXoRs21iw==", "requires": { "ndarray-extract-contour": "^1.0.0", "triangulate-hypercube": "^1.0.0", @@ -11631,9 +30652,9 @@ "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==" }, "svg-path-bounds": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.1.tgz", - "integrity": "sha1-v0WLeDcmv1NDG0Yz8nkvYHSNn3Q=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz", + "integrity": "sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ==", "requires": { "abs-svg-path": "^0.1.1", "is-svg-path": "^1.0.1", @@ -11642,9 +30663,9 @@ }, "dependencies": { "normalize-svg-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.0.1.tgz", - "integrity": "sha1-b3Ka1rcLtMpO/y/ksQdInv4dVv4=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", "requires": { "svg-arc-to-cubic-bezier": "^3.0.0" } @@ -11664,53 +30685,64 @@ } }, "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "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==", + "dev": true + }, + "table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dev": true, "requires": { - "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" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } } } }, @@ -11743,107 +30775,54 @@ "resolve": "^1.14.2" }, "dependencies": { - "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" + "color-convert": "^2.0.1" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "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": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "color-name": "~1.1.4" } } } }, - "browserslist": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.4.tgz", - "integrity": "sha512-d7rCxYV8I9kj41RH8UKYnvDYCRENUlHRgyXy/Rhr/1BaeLGfiCptEdFE8MIrvGfWbBFNjVYx76SQWvNX1j+/cQ==", + "autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", "requires": { - "caniuse-lite": "^1.0.30001208", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.712", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" } }, - "caniuse-lite": { - "version": "1.0.30001208", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", - "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==" - }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.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" - } - }, - "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" - } - } } }, - "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==", + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "requires": { - "color-name": "~1.1.4" + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, "color-name": { @@ -11851,20 +30830,42 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "electron-to-chromium": { - "version": "1.3.712", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz", - "integrity": "sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==" + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } }, - "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==" + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" }, "postcss": { "version": "7.0.39", @@ -11875,6 +30876,15 @@ "source-map": "^0.6.1" } }, + "postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "requires": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, "postcss-nested": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", @@ -11882,99 +30892,53 @@ "requires": { "postcss": "^7.0.32", "postcss-selector-parser": "^6.0.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - } } }, - "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==" + "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" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" } } }, "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "tape": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/tape/-/tape-4.13.0.tgz", - "integrity": "sha512-J/hvA+GJnuWJ0Sj8Z0dmu3JgMNU+MmusvkCT7+SN4/2TklW18FNCp/UuHIEhPZwHfy4sXfKYgC7kypKg4umbOw==", - "requires": { - "deep-equal": "~1.1.1", - "defined": "~1.0.0", - "dotignore": "~0.1.2", - "for-each": "~0.3.3", - "function-bind": "~1.1.1", - "glob": "~7.1.6", - "has": "~1.0.3", - "inherits": "~2.0.4", - "is-regex": "~1.0.5", - "minimist": "~1.2.0", - "object-inspect": "~1.7.0", - "resolve": "~1.14.2", - "resumer": "~0.0.0", - "string.prototype.trim": "~1.2.1", - "through": "~2.3.8" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "resolve": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", - "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==", - "requires": { - "path-parse": "^1.0.6" - } - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" } }, "terser": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.4.tgz", - "integrity": "sha512-5fqgBPLgVHZ/fVvqRhhUp9YUiGXhFJ9ZkrZWD9vQtFBR4QIGTnbsb+/kKqSqfgp3WnBwGWAFnedGTtmX1YTn0w==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", + "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map-support": "~0.5.20" }, "dependencies": { "commander": { @@ -11986,47 +30950,132 @@ } }, "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, + "dependencies": { + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } } }, "text-cache": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.2.1.tgz", - "integrity": "sha512-G52NFRYXEW9BL4E3kBPquefXql9OT3sNT4J16gcpl3/a8y/YioDOR2Iwga5rNs9tY7rH2xv6rF8fAYrbINn6Kg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.2.2.tgz", + "integrity": "sha512-zky+UDYiX0a/aPw/YTBD+EzKMlCTu1chFuCMZeAkgoRiceySdROu1V2kJXhCbtEdBhiOviYnAdGiSYl58HW0ZQ==", "requires": { "vectorize-text": "^3.2.1" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "peer": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "peer": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-loader": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-3.0.4.tgz", + "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==", + "dev": true, + "peer": true, + "requires": { + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "peer": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, "three": { - "version": "0.83.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.83.0.tgz", - "integrity": "sha1-O3+UeQrz4CHawfRKJhdWnKIDKws=" + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.143.0.tgz", + "integrity": "sha512-oKcAGYHhJ46TGEuHjodo2n6TY2R6lbvrkp+feKZxqsUL/WkH7GKKaeu6RHeyb2Xjfk2dPLRKLsOP0KM2VgT8Zg==" }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true }, "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + } } }, "thunky": { @@ -12035,25 +31084,10 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, "tinycolor2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", - "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", + "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==" }, "tinyqueue": { "version": "2.0.3", @@ -12070,65 +31104,33 @@ "string-to-arraybuffer": "^1.0.0" } }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true }, "to-float32": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.0.1.tgz", - "integrity": "sha512-nOy2WSwae3xhZbc+05xiCuU3ZPPmH0L4Rg4Q1qiOGFSuNSCTB9nVJaGgGl3ZScxAclX/L8hJuDHJGDAzbfuKCQ==" - }, - "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=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.1.0.tgz", + "integrity": "sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg==" }, "to-px": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.1.0.tgz", - "integrity": "sha512-bfg3GLYrGoEzrGoE05TAL/Uw+H/qrf2ptr9V3W7U0lkjjyYnIfgxmVLUfhQ1hZpIQwin81uxhDjvUkDYsC0xWw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.0.1.tgz", + "integrity": "sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw==", "requires": { "parse-unit": "^1.0.1" } }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, "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=", + "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==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "to-uint8": { @@ -12144,29 +31146,64 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, "topojson-client": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz", - "integrity": "sha1-/59784mRGF4LQoTCsGroNPDqxsg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", "requires": { "commander": "2" } }, "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "dev": true + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", "dev": true }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, "triangulate-hypercube": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/triangulate-hypercube/-/triangulate-hypercube-1.0.1.tgz", - "integrity": "sha1-2Acdsuv8/VHzCNC88qXEils20Tc=", + "integrity": "sha512-SAIacSBfUNfgeCna8q2i+1taOtFJkYuOqpduaJ1KUeOJpqc0lLKMYzPnZb4CA6KCOiD8Pd4YbuVq41wa9dvWyw==", "requires": { "gamma": "^0.1.0", "permutation-parity": "^1.0.0", @@ -12176,33 +31213,21 @@ "triangulate-polyline": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz", - "integrity": "sha1-v4uod6hQVBA/65+lphtOjXAXgU0=", + "integrity": "sha512-crJcVFtVPFYQ8r9iIhe9JqkauDvNWDSZLot8ly3DniSCO+zyUfKbtfD3fEoBaA5uMrQU/zBi11NBuVQeSToToQ==", "requires": { "cdt2d": "^1.0.0" } }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, "tslib": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", - "integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "turntable-camera-controller": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/turntable-camera-controller/-/turntable-camera-controller-3.0.1.tgz", - "integrity": "sha1-jb0/4AVQGRxlFky4iJcQSVeK/Zk=", + "integrity": "sha512-UOGu9W/Mx053pAaczi0BEPqvWJOqSgtpdigWG9C8dX8rQVdyl2hWmpdJW3m15QrGxJtJHIhhDTHVtTZzPkd/FA==", "requires": { "filtered-vector": "^1.2.1", "gl-mat4": "^1.0.2", @@ -12212,12 +31237,12 @@ "two-product": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz", - "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo=" + "integrity": "sha512-vOyrqmeYvzjToVM08iU52OFocWT6eB/I5LUWYnxeAPGXAhAxXYU/Yr/R2uY5/5n4bvJQL9AQulIuxpIsMoT8XQ==" }, "two-sum": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz", - "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=" + "integrity": "sha512-phP48e8AawgsNUjEY2WvoIWqdie8PoiDZGxTDv70LDr01uX5wLEQbOgSP7Z/B6+SW5oLtbe8qaYX2fKJs3CGTw==" }, "type": { "version": "1.2.0", @@ -12227,11 +31252,23 @@ "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "requires": { "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -12245,12 +31282,12 @@ "type-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/type-name/-/type-name-2.0.2.tgz", - "integrity": "sha1-7+fUEj2KxSr/9/QMfk3sUmYAj7Q=" + "integrity": "sha512-kkgkuqR/jKdKO5oh/I2SMu2dGbLXoJq0zkdgbxaqYK+hr9S9edwVVGf+tMUFTx2gH9TN2+Zu9JZ/Njonb3cjhA==" }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "typedarray-pool": { "version": "1.2.0", @@ -12279,29 +31316,31 @@ } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, "uglymol": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/uglymol/-/uglymol-0.5.7.tgz", - "integrity": "sha1-fTMVJKOuBvnnP+QIL24EWip9Y+U=", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/uglymol/-/uglymol-0.6.4.tgz", + "integrity": "sha512-ZjGNsJbLUt42ETlC7I7yjGPNR6OzdzYeCrefslVHHdEgFsaPKbKtA6OaSzyGlT6zyNuzYYK6uEDuPtgBYfyAuA==" + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "requires": { - "three": "~0.83.0" + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" } }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + "integrity": "sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==" }, "underscore-template-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/underscore-template-loader/-/underscore-template-loader-1.0.0.tgz", - "integrity": "sha512-4tzm3DY8nLD6mKCQmPk51vu5HW8T61fSGpfkUrlrwF0oOPTinL8Zk7W08xHOmCZ2Sh9bZHVMzVgr/DUM+10iUA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/underscore-template-loader/-/underscore-template-loader-1.1.0.tgz", + "integrity": "sha512-fTN35hbcv+fi31sVZp8X9Bsi0oIspdzsj/jZeehdRgDSW3w2TnpBVL1wHaYZC8xSeIDA1poYPR5ceB0forJPIA==", "dev": true, "requires": { "fastparse": "^1.1.1", @@ -12317,19 +31356,19 @@ "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", "dev": true }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", "dev": true }, "loader-utils": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", "dev": true, "requires": { "big.js": "^3.1.3", @@ -12340,57 +31379,15 @@ } } }, - "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==" - }, - "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==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==" - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" - }, "union-find": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz", - "integrity": "sha1-KSusQV5q06iVNdI3AQ20pTYoTlg=" - }, - "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==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } + "integrity": "sha512-wFA9bMD/40k7ZcpKVXfu6X1qD3ri5ryO8HUsuA1RnxPCQl66Mu6DgkxyR+XNnd+osD0aLENixcJVFj+uf+O4gw==" }, "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" }, "unique-filename": { "version": "1.1.1", @@ -12410,193 +31407,115 @@ "imurmurhash": "^0.1.4" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "peer": true }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, "unquote": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, + "update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, "update-diff": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz", - "integrity": "sha1-9RAYLYHugZ+4LDprIrYrve2ngI8=" + "integrity": "sha512-rCiBPiHxZwT4+sBhEbChzpO5hYHjm91kScWgdHf4Qeafs6Ba7MBl+d9GlGv72bcTZQO0sLmtQS1pHSWoCLtN/A==" }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", "dev": true }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, "url-loader": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", - "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dev": true, "requires": { - "loader-utils": "^1.2.3", - "mime": "^2.4.4", - "schema-utils": "^2.5.0" + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" }, "dependencies": { - "schema-utils": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", - "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } } } }, "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } + "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" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", "dev": true }, "utils-copy": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/utils-copy/-/utils-copy-1.1.1.tgz", - "integrity": "sha1-biuXmCqozXPhGCo+b4vsPA9AWKc=", + "integrity": "sha512-+NhJVV+PcxjdpkMrVTqXhQHPldlFGca5XR9YnGyNn7kQ0fMi+DqNLzdnhJ4TJ1HNy/HzB7c+FPg3y+4icY99ZA==", "requires": { "const-pinf-float64": "^1.0.0", "object-keys": "^1.0.9", @@ -12612,7 +31531,7 @@ "utils-copy-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-copy-error/-/utils-copy-error-1.0.1.tgz", - "integrity": "sha1-eR3jk8DwmJCv1Z88vqY18HmpT6U=", + "integrity": "sha512-RbJcGPZ6Ru2HQk9SWkvbdWNPX58pt4MO5uXsOQRu4LEGWB3LglkRrmnE/Ph1qWg6ywQ0qj95wTz1OeqQ2l8DCA==", "requires": { "object-keys": "^1.0.9", "utils-copy": "^1.1.0" @@ -12621,7 +31540,7 @@ "utils-indexof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utils-indexof/-/utils-indexof-1.0.0.tgz", - "integrity": "sha1-IP6r8J7xAYtSNkPoOA57yD7GG1w=", + "integrity": "sha512-76QBfRJpn4A0P5uTO1x00x+Yog36w2Pab0n+aT9UfUvVa4l+e8k3p7YwNpDvfQ6+aKGZdxZpxcNotNS4YjFcyg==", "requires": { "validate.io-array-like": "^1.0.1", "validate.io-integer-primitive": "^1.0.0" @@ -12630,39 +31549,49 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "utils-regex-from-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utils-regex-from-string/-/utils-regex-from-string-1.0.0.tgz", - "integrity": "sha1-/hopCfjeD/DVGCyA+8ZU1qaH0Yk=", + "integrity": "sha512-xKfdmEF19iUu9TKxFiohQUlQTuqYdV80/CxHiudVI37iEV/OA4HHlXZoc4qvuO1B74EcBVpErBreRO/dpdLeYA==", "requires": { "regex-regex": "^1.0.0", "validate.io-string-primitive": "^1.0.0" } }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "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==", "dev": true }, + "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==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "validate.io-array": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", - "integrity": "sha1-W1osr9j4uFq7L4hroVPy2Tond00=" + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==" }, "validate.io-array-like": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/validate.io-array-like/-/validate.io-array-like-1.0.2.tgz", - "integrity": "sha1-evn363tRcVvrIhVmjsXM5U+t21o=", + "integrity": "sha512-rGLiN0cvY9OWzQcWP+RtqZR/MK9RUz3gKDTCcRLtEQ/BvlanMF5PyqtVIN+CgrIBCv/ypfme9v7r4yMJPYpbNA==", "requires": { "const-max-uint32": "^1.0.2", "validate.io-integer-primitive": "^1.0.0" @@ -12671,12 +31600,12 @@ "validate.io-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/validate.io-buffer/-/validate.io-buffer-1.0.2.tgz", - "integrity": "sha1-hS1nNAIZFNXROvwyUxdh43IO1E4=" + "integrity": "sha512-6Tad+/QYOxWEXsesKYak1mHOzGdPYS4QeHFImWn7ECi4GR0x3vh7+6+1yoLKNXiklKuTFOxHLG3kZy9tPX0GvQ==" }, "validate.io-integer": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", - "integrity": "sha1-FoSWSAuVviJH7EQ/IjPeT4mHgGg=", + "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", "requires": { "validate.io-number": "^1.0.3" } @@ -12684,7 +31613,7 @@ "validate.io-integer-primitive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-integer-primitive/-/validate.io-integer-primitive-1.0.0.tgz", - "integrity": "sha1-qaoBA1X+hoHA/qbBp0rSQZyt3cY=", + "integrity": "sha512-4ARGKA4FImVWJgrgttLYsYJmDGwxlhLfDCdq09gyVgohLKKRUfD3VAo1L2vTRCLt6hDhDtFKdZiuYUTWyBggwg==", "requires": { "validate.io-number-primitive": "^1.0.0" } @@ -12692,17 +31621,17 @@ "validate.io-matrix-like": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/validate.io-matrix-like/-/validate.io-matrix-like-1.0.2.tgz", - "integrity": "sha1-XsMqddCInaxzbepovdYUWxVe38M=" + "integrity": "sha512-86mqLUIkZCRAOVKZvpCB7sDCw1dKBjBkY+C6WO/wLo/jQx0sOqQZz3LLtDw0DCfuAKxRuhSmIpX3nzr0nWrbdw==" }, "validate.io-ndarray-like": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-ndarray-like/-/validate.io-ndarray-like-1.0.0.tgz", - "integrity": "sha1-2KOw7RZbvx0vwNAHMnDPpVIpWRk=" + "integrity": "sha512-OV85AosxraPFSXJwzv/d7Cu5/dLiyLtsHmxtHTJcHW1N0uscd0eJ2df1Zk+HdID0eUctUllW/1YuQPUJFv1pTA==" }, "validate.io-nonnegative-integer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-nonnegative-integer/-/validate.io-nonnegative-integer-1.0.0.tgz", - "integrity": "sha1-gGkkOgjF+Y6VQTySnf17GPP28p8=", + "integrity": "sha512-uOMekPwcl84yg8NR7zgIZCZ9pHCtd9CK1Ri51N+ZJLTe1HyLbmdFdy7ZmfkiHkMvB1pOxeQmd1/LBjKhUD1L3A==", "requires": { "validate.io-integer": "^1.0.5" } @@ -12710,17 +31639,17 @@ "validate.io-number": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", - "integrity": "sha1-9j/+2iSL8opnqNSODjtGGhZluvg=" + "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==" }, "validate.io-number-primitive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-number-primitive/-/validate.io-number-primitive-1.0.0.tgz", - "integrity": "sha1-0uAfICmJNp3PEVVElWQgOv5YTlU=" + "integrity": "sha512-8rlCe7N0TRTd50dwk4WNoMXNbX/4+RdtqE3TO6Bk0GJvAgbQlfL5DGr/Pl9ZLbWR6CutMjE2cu+yOoCnFWk+Qw==" }, "validate.io-positive-integer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/validate.io-positive-integer/-/validate.io-positive-integer-1.0.0.tgz", - "integrity": "sha1-ftLQO0wnVYzGagCqsPDpIYFKZYI=", + "integrity": "sha512-eg4LSdyqjICNUZWRilcQ5l+YayRlu6yi+GQsWw1bCmtG9yayOPmLa1fPymEHPPhbvWPAv3w0LLbCsf03pBHZkg==", "requires": { "validate.io-integer": "^1.0.5" } @@ -12728,23 +31657,23 @@ "validate.io-string-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/validate.io-string-primitive/-/validate.io-string-primitive-1.0.1.tgz", - "integrity": "sha1-uBNbn7E3K94C/dU60dDM1t55j+4=" + "integrity": "sha512-TORbkLMdOFkEbPtfdx76FSVQGSAzyUEMxI+pBq5pfFm1ZzIesP+XiGc6eIK75aKu7RA7a8EcqUv5OrY5wfog5w==" }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, "vectorize-text": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.2.1.tgz", - "integrity": "sha512-rGojF+D9BB96iPZPUitfq5kaiS6eCJmfEel0NXOK/MzZSuXGiwhoop80PtaDas9/Hg/oaox1tI9g3h93qpuspg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.2.2.tgz", + "integrity": "sha512-34NVOCpMMQVXujU4vb/c6u98h6djI0jGdtC202H4Huvzn48B6ARsR7cmGh1xsAc0pHNQiUKGK/aHF05VtGv+eA==", "requires": { "cdt2d": "^1.0.0", "clean-pslg": "^1.1.0", "ndarray": "^1.0.11", - "planar-graph-to-polyline": "^1.0.0", + "planar-graph-to-polyline": "^1.0.6", "simplify-planar-graph": "^2.0.1", "surface-nets": "^1.0.0", "triangulate-polyline": "^1.0.0" @@ -12755,32 +31684,77 @@ "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-2.2.15.tgz", "integrity": "sha512-4TOsI8XwVkKVLkg8Nhmy+jyoJrR6XcTRDyxBarzcCvYzU61zamipS1WsB6FlDze8eJQpgglS4NXAS6o4NDPs1g==" }, - "vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", - "dev": true - }, - "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==", - "dev": true - }, "vt-pbf": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.1.tgz", - "integrity": "sha512-pHjWdrIoxurpmTcbfBWXaPwSmtPAHS105253P1qyEfSTV2HJddqjM+kIHquaT/L6lVJIk9ltTGc0IxR/G47hYA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", "requires": { "@mapbox/point-geometry": "0.1.0", "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.0.5" + "pbf": "^3.2.1" } }, "vue": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", - "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" + "version": "2.7.10", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz", + "integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==", + "requires": { + "@vue/compiler-sfc": "2.7.10", + "csstype": "^3.1.0" + } + }, + "vue-eslint-parser": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz", + "integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } }, "vue-hot-reload-api": { "version": "2.3.4", @@ -12789,9 +31763,9 @@ "dev": true }, "vue-loader": { - "version": "15.9.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.0.tgz", - "integrity": "sha512-FeDHvTSpwyLeF7LIV1PYkvqUQgTJ8UmOxhSlCyRSxaXCKk+M6NF4tDQsLsPPNeDPyR7TfRQ8MLg6v+8PsDV9xQ==", + "version": "15.10.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", + "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", "dev": true, "requires": { "@vue/component-compiler-utils": "^3.1.0", @@ -12799,31 +31773,47 @@ "loader-utils": "^1.1.0", "vue-hot-reload-api": "^2.3.0", "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } } }, "vue-router": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.1.tgz", - "integrity": "sha512-RRQNLT8Mzr8z7eL4p7BtKvRaTSGdCbTy2+Mm5HTJvLGYSSeG9gDzNasJPP/yOYKLy+/cLG/ftrqq5fvkFwBJEw==" + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz", + "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==" }, "vue-style-loader": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", - "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", "dev": true, "requires": { "hash-sum": "^1.0.2", "loader-utils": "^1.0.2" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } } }, "vue-template-compiler": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", - "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "version": "2.7.10", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz", + "integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==", "dev": true, "requires": { "de-indent": "^1.0.2", - "he": "^1.1.0" + "he": "^1.2.0" } }, "vue-template-es2015-compiler": { @@ -12835,17 +31825,35 @@ "vuex": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", - "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==" + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "requires": {} + }, + "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==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } }, "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" } }, "wbuf": { @@ -12857,481 +31865,411 @@ "minimalistic-assert": "^1.0.0" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, "weak-map": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", - "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", + "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==" }, "weakmap-shim": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/weakmap-shim/-/weakmap-shim-1.1.1.tgz", - "integrity": "sha1-1lr9eEEJshZuAP9XHDMVDsKkC0k=" + "integrity": "sha512-/wNyG+1FpiHhnfQo+TuA/XAUpvOOkKVl0A4qpT+oGcj5SlZCLmM+M1Py/3Sj8sy+YrEauCVITOxCsZKo6sPbQg==" }, "webgl-context": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz", - "integrity": "sha1-jzfXJXz23xzQpJ5qextyG5TMhqA=", + "integrity": "sha512-q/fGIivtqTT7PEoF07axFIlHNk/XCPaYpq64btnepopSWvKNFkoORlQYgqDigBIuGA1ExnFd/GnSUnBNEPQY7Q==", "requires": { "get-canvas-context": "^1.0.1" } }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, "webpack": { - "version": "4.41.6", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz", - "integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - } - }, - "webpack-bundle-analyzer": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz", - "integrity": "sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-walk": "^6.1.1", - "bfj": "^6.1.1", - "chalk": "^2.4.1", - "commander": "^2.18.0", - "ejs": "^2.6.1", - "express": "^4.16.3", - "filesize": "^3.6.1", - "gzip-size": "^5.0.0", - "lodash": "^4.17.15", - "mkdirp": "^0.5.1", - "opener": "^1.5.1", - "ws": "^6.0.0" + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" }, "dependencies": { - "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==", + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true } } }, - "webpack-cli": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", - "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", + "webpack-bundle-analyzer": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz", + "integrity": "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==", "dev": true, "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "color-convert": "^2.0.1" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "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": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "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": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" + "color-name": "~1.1.4" } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "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 }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "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 }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "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": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "has-flag": "^4.0.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "requires": {} + } + } + }, + "webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "dev": true, + "peer": true, + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" + "shebang-regex": "^3.0.0" } }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "isexe": "^2.0.0" } } } }, "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "dev": true, "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - } - }, - "webpack-dev-server": { - "version": "3.10.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz", - "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.6", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" + "schema-utils": "^4.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "requires": { - "ms": "^2.1.1" + "fast-deep-equal": "^3.1.3" } }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "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==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { - "invert-kv": "^2.0.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + } + } + }, + "webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "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" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "picomatch": "^2.2.1" } }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } } } }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" } }, "webpack-sources": { @@ -13344,27 +32282,72 @@ "source-map": "~0.6.1" } }, + "webpack-virtual-modules": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.5.tgz", + "integrity": "sha512-8bWq0Iluiv9lVf9YaqWQ9+liNgXSHICm+rg544yRgGYaR8yXZTVBaHZkINZSB2yZSWo4b0F6MIxqJezVfOEAlg==", + "dev": true, + "peer": true + }, "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, "requires": { - "http-parser-js": ">=0.4.0 <0.4.11", + "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, - "wgs84": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz", - "integrity": "sha1-NP3FVZF7blfPKigu0ENxDASc3HY=" + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "dev": true, + "peer": true + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } }, "which": { "version": "1.3.1", @@ -13375,61 +32358,164 @@ "isexe": "^2.0.0" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + "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==", + "requires": { + "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" + } }, - "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==" + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + "which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + } }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "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, "requires": { - "errno": "~0.1.7" + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "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==" + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "world-calendars": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.3.tgz", - "integrity": "sha1-slxQMrokEo/8QdCfr0pewbnBQzU=", + "integrity": "sha512-sAjLZkBnsbHkHWVhrsCU5Sa/EVuf9QqgvrN8zyJ2L/F9FR9Oc6CvVK0674+PGAtmmmYQMH98tCUSO4QLQv3/TQ==", "requires": { "object-assign": "^4.1.0" } }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.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==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + }, + "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 + } } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true }, "xtend": { "version": "4.0.2", @@ -13437,15 +32523,15 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "yaml": { @@ -13454,10 +32540,57 @@ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + } + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, "zero-crossings": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz", - "integrity": "sha1-xWK9MRNkPzRDokXRJAa4i2m5qf8=", + "integrity": "sha512-iNIldMZaDtAyIJMJ8NnGVHeejH//y4eVmpXriM+q/B/BPNz+2E7oAgSnw9MXqCd3RbQ8W+hor7T2jEyRoc/s2A==", "requires": { "cwise-compiler": "^1.0.0" } @@ -13465,7 +32598,7 @@ "zlibjs": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.1.8.tgz", - "integrity": "sha1-CF3ZRAcfDVaAbvlLZpUTudRg0mc=" + "integrity": "sha512-K6TlrudzpjDHG6/uzyq8X9/2UFrcSLKhqxZc2nP1fc85vJdhPVehsyElowkI6sqYdF/QA3HA81oEBBLKXCyENA==" } } } diff --git a/client/package.json b/client/package.json index b75b19c93..776142436 100644 --- a/client/package.json +++ b/client/package.json @@ -1,34 +1,43 @@ { "name": "synchweb-webpack", - "version": "1.0.0", + "version": "1.1.0", "description": "", "private": true, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.config.js -p --mode=production", - "build:dev": "./node_modules/.bin/webpack --config webpack.config.js --mode development", + "lint": "eslint --ext .js src/js", + "lint-vue": "eslint -c .eslintrc-vue.js --no-eslintrc --ext .vue src/js", + "test": "jest tests/unit", + "build": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.config.js --mode production", + "build:dev": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.config.js --mode development", "build:stats": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.config.js --mode production --profile --json > stats.json", "analyze": "./node_modules/.bin/webpack-bundle-analyzer stats.json", - "serve": "./node_modules/.bin/webpack-dev-server" + "serve": "NODE_ENV=development ./node_modules/.bin/webpack-dev-server --mode development" }, "keywords": [ "SynchWeb", "ISPyB" ], - "author": "Dr Neil A Smith", + "author": "Diamond Light Source", "license": "Apache-2.0", "devDependencies": { - "@fullhuman/postcss-purgecss": "^2.3.0", + "@babel/preset-env": "^7.21.5", + "@vue/test-utils": "^1.3.5", + "@vue/vue2-jest": "^28.1.0", + "@webpack-cli/serve": "^1.7.0", "autoprefixer": "^10.0.2", - "copy-webpack-plugin": "^5.0.4", - "css-loader": "^3.1.0", - "file-loader": "^4.0.0", - "git-revision-webpack-plugin": "^3.0.3", + "copy-webpack-plugin": "^6.4.1", + "css-loader": "^5.2.7", + "css-minimizer-webpack-plugin": "^4.1.0", + "eslint": "^7.32.0", + "eslint-loader": "^4.0.2", + "eslint-plugin-backbone": "^2.1.1", + "eslint-plugin-vue": "^9.5.1", + "file-loader": "^6.2.0", "html-loader": "^0.5.5", - "html-webpack-plugin": "^3.2.0", + "html-webpack-plugin": "^5.5.0", "imports-loader": "^0.8.0", - "mini-css-extract-plugin": "^0.8.0", - "optimize-css-assets-webpack-plugin": "^5.0.3", + "jest-environment-jsdom": "^29.5.0", + "mini-css-extract-plugin": "^2.6.1", "postcss": "^8.1.14", "postcss-color-function": "^4.1.0", "postcss-extend-rule": "^3.0.0", @@ -40,15 +49,15 @@ "postcss-simple-vars": "^6.0.1", "postcss-strip-inline-comments": "^0.1.5", "postcss-utilities": "^0.8.4", - "raw-loader": "^1.0.0", + "raw-loader": "^4.0.2", "underscore-template-loader": "^1.0.0", - "url-loader": "^2.1.0", - "vue-loader": "^15.7.1", + "url-loader": "^4.1.1", + "vue-loader": "^15.10.0", "vue-template-compiler": "^2.6.10", - "webpack": "^4.40.2", - "webpack-bundle-analyzer": "^3.4.1", - "webpack-cli": "^3.3.9", - "webpack-dev-server": "^3.8.2" + "webpack": "^5.74.0", + "webpack-bundle-analyzer": "^4.6.1", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.11.1" }, "dependencies": { "@highcharts/map-collection": "^1.1.2", @@ -57,10 +66,12 @@ "backbone.marionette": "2.1.0", "backbone.paginator": "2.0.0", "backbone.syphon": "0.5.0", - "backgrid": "0.3.5", - "backgrid-paginator": "0.3.5", + "backgrid": "0.3.8", + "backgrid-paginator": "0.3.9", "backgrid-select-all": "0.3.5", + "ci": "^2.2.0", "csv-file-validator": "^1.7.4", + "d3-color": "^2.0.0", "d3-scale": "^3.2.3", "d3-scale-chromatic": "^2.0.0", "d3-selection": "^2.0.0", @@ -86,13 +97,39 @@ "portal-vue": "2.1.7", "promise": "^8.0.3", "tailwindcss": "^1.9.5", - "three": "^0.83.0", - "uglymol": "^0.5.7", - "underscore": "1.6.0", + "three": "^0.143.0", + "uglymol": "^0.6.4", + "underscore": "1.8.3", + "util": "^0.12.4", "vee-validate": "^2.2.15", "vue": "^2.6.10", "vue-router": "^3.4.3", "vuex": "^3.5.1", "zlibjs": "^0.1.7" + }, + "engines": { + "node": "^18.0.0" + }, + "jest": { + "testEnvironment": "jsdom", + "moduleDirectories": [ + "node_modules", + "src/js" + ], + "moduleNameMapper": { + "marionette": "backbone.marionette/lib/backbone.marionette.min" + }, + "moduleFileExtensions": [ + "js", + "json", + "vue" + ], + "transform": { + "^.+\\.js$": "babel-jest", + "^.+\\.vue$": "@vue/vue2-jest" + }, + "transformIgnorePatterns": [ + "node_modules/(?!lodash-es)" + ] } } diff --git a/client/postcss.config.js b/client/postcss.config.js index 0a0dd3012..f0959e0e5 100644 --- a/client/postcss.config.js +++ b/client/postcss.config.js @@ -1,18 +1,5 @@ // postcss.config.js const path = require('path'); -// Disabling purgecss for now because it strips too many styles. -// TODO - figure out the correct extractor to parse backbone templates (inline and html) -// const purgecss = require('@fullhuman/postcss-purgecss')({ - -// // Specify the paths to all of the template files in your project -// content: [ -// './src/index.php', -// './src/**/*.html', -// './src/**/*.vue', -// './src/**/*.js', /* Lots of classes are used within inline templates... */ -// ], -// defaultExtractor: content => content.match(/[\w-/:]+(? ( { @@ -31,8 +18,6 @@ module.exports = ({env}) => ( require('postcss-extend-rule'), require('tailwindcss'), require('autoprefixer'), - // Disabling purgecss for now - strips out too many styles - // ...process.env.NODE_ENV === 'production' ? [purgecss]: [], ] } ) diff --git a/client/src/css/main.scss b/client/src/css/main.scss index 7b3670a36..28efbec82 100644 --- a/client/src/css/main.scss +++ b/client/src/css/main.scss @@ -24,6 +24,8 @@ @import "partials/_imaging.scss"; + + // Third-party @import "jquery-ui/themes/base/all.css"; @import "jquery-ui-timepicker-addon/dist/jquery-ui-timepicker-addon.css"; diff --git a/client/src/css/modules/_colors.scss b/client/src/css/modules/_colors.scss index c6b40749b..29edacd74 100644 --- a/client/src/css/modules/_colors.scss +++ b/client/src/css/modules/_colors.scss @@ -3,7 +3,7 @@ $body-background: #000000; $body-color: #000000; // Postcss color and vars workaround $body-color-tint: color(#000000 tint(60%)); -$body-color-tint35: color(#000, tint(35%)); +$body-color-tint35: color(#000 tint(35%)); // Default Links $link-color: #666666; diff --git a/client/src/css/partials/_content.scss b/client/src/css/partials/_content.scss index 375b9b363..99767d875 100644 --- a/client/src/css/partials/_content.scss +++ b/client/src/css/partials/_content.scss @@ -866,7 +866,7 @@ li:last-child .visit_users { font-weight: bold; background: $content-filter-current-background; padding: 5px; - text-align: center; + text-align: left; } } diff --git a/client/src/css/partials/_imaging.scss b/client/src/css/partials/_imaging.scss index 26b35bf0b..0fc3ade5b 100644 --- a/client/src/css/partials/_imaging.scss +++ b/client/src/css/partials/_imaging.scss @@ -220,3 +220,9 @@ div.img_history { input[name=gap] { width: 30px; } + +.plate-12-wide li { + @mixin cols 12, 2% 0%, 0.2%; + text-align: center; + box-sizing: content-box; +} diff --git a/client/src/css/partials/_tables.scss b/client/src/css/partials/_tables.scss index fbd9cdbff..17b58992d 100644 --- a/client/src/css/partials/_tables.scss +++ b/client/src/css/partials/_tables.scss @@ -303,6 +303,13 @@ They can be overriden by specific classes below } } + thead th { + button { + color:white; + background-color: transparent; + } + } + table.subsamples { table td { text-align: left; diff --git a/client/src/css/partials/_utility.scss b/client/src/css/partials/_utility.scss index e75beb96f..54f59304a 100644 --- a/client/src/css/partials/_utility.scss +++ b/client/src/css/partials/_utility.scss @@ -60,6 +60,18 @@ text-align: right; } +.flex { + display: flex; +} + +.flex_take_space { + flex: 1 1 auto +} + +.flex_dont_change { + flex: 0 0 auto +} + /* TODO: If we can ever get rid of this class, we can also remove the work-around in plotly support which needs to circumvent it */ .active { @@ -176,6 +188,10 @@ &.button-highlight { background: linear-gradient($content-highlight, $content-page-background); } + + &:disabled { + cursor: not-allowed; + } } .no-close { diff --git a/client/src/images/puck_no_labels_470x470.png b/client/src/images/puck_no_labels_470x470.png new file mode 100644 index 000000000..eba99ea69 Binary files /dev/null and b/client/src/images/puck_no_labels_470x470.png differ diff --git a/client/src/js/app.js b/client/src/js/app.js index 2e4bf6749..db71ee3a6 100644 --- a/client/src/js/app.js +++ b/client/src/js/app.js @@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +/* eslint no-undef: "off"*/ define(['backbone', 'marionette', 'underscore', 'jquery', @@ -32,8 +33,6 @@ function(Backbone, Marionette, _, $, HeaderView, SideBarView, DialogRegion, Logi window.app = new Marionette.Application() - console.log('CONFIG', config) - // Moved to here so its available on app start app.options = new Options() @@ -58,8 +57,6 @@ function(Backbone, Marionette, _, $, HeaderView, SideBarView, DialogRegion, Logi pairs[kv[0]] = kv[1] }) - console.log('pairs', pairs) - if ('prop' in pairs) sessionStorage.setItem('prop', pairs.prop) } @@ -108,8 +105,8 @@ function(Backbone, Marionette, _, $, HeaderView, SideBarView, DialogRegion, Logi // JSON content } else if (options.contentType == 'application/json' || options.type == 'DELETE') { - if (options.data) var tmp = JSON.parse(options.data) - else var tmp = {} + var tmp = {} + if (options.data) tmp = JSON.parse(options.data) if (Array.isArray(tmp)) tmp[0].prop = prop else { @@ -167,7 +164,7 @@ function(Backbone, Marionette, _, $, HeaderView, SideBarView, DialogRegion, Logi try { json = $.parseJSON(xhr.responseText) } catch(err) { - + console.error("Error parsing response text: ", err) } } var msg = json && (json.error || json.msg) ? (json.error ? json.error : json.msg) : error @@ -449,7 +446,6 @@ app.clearVisit = function(){ // Only need this for pushState enabled browsers if (Backbone.history && Backbone.history._hasPushState) { var $document = $(window.document); - var openLinkInTab = false; var is_relative_to_page = function(href) { return href.match(/^\/|(http:|https:|ftp:|mailto:|javascript:)/) === null; @@ -460,16 +456,6 @@ app.clearVisit = function(){ return href.indexOf("#") === -1 && (is_relative_to_page(href) || href.indexOf(Backbone.history.root) == 0 || href.indexOf('/') == 0) && (href.indexOf(app.apiurl) != 0); }; - $document.keydown(function(e) { - if (e.ctrlKey || e.keyCode === 91) { - openLinkInTab = true; - } - }); - - $document.keyup(function(e) { - openLinkInTab = false; - }); - $document.on("click", "a", function(e) { var href = $(this).attr("href"); if (!href) return diff --git a/client/src/js/app/components/base-button.vue b/client/src/js/app/components/base-button.vue new file mode 100644 index 000000000..064924076 --- /dev/null +++ b/client/src/js/app/components/base-button.vue @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/client/src/js/app/components/base-input-checkbox.vue b/client/src/js/app/components/base-input-checkbox.vue index 40b54dd4d..44398fc5a 100644 --- a/client/src/js/app/components/base-input-checkbox.vue +++ b/client/src/js/app/components/base-input-checkbox.vue @@ -12,28 +12,45 @@ Slots include:
- {{ value }} Edit + {{ value }} Edit -
@@ -42,8 +59,8 @@ export default { name: "BaseInputCheckbox", props: { value: { // Passed in automatically if v-model used - type: Boolean, - required: true, + type: [String, Boolean, Number], + required: true }, id: { type: String, @@ -84,7 +101,7 @@ export default { }, watch: { editable: function(value) { - if (value === false) this.showEditIcon = false + if (!value) this.showEditIcon = false } }, created() { diff --git a/client/src/js/app/components/base-input-combobox.vue b/client/src/js/app/components/base-input-combobox.vue index 7d03863fa..a257864ba 100644 --- a/client/src/js/app/components/base-input-combobox.vue +++ b/client/src/js/app/components/base-input-combobox.vue @@ -16,11 +16,16 @@ Slots include: --> @@ -145,6 +184,15 @@ export default { comboBoxParent: null, } }, + computed: { + // If a user passes in an error Message, add the error class to the input + classObject() { + return [ this.inputClass, this.errorMessage ? this.errorClass : ''] + }, + inlineText() { + return this.initialText || this.localValue + } + }, watch: { // Because we are using a cached local value (for inline edit mode) we should react to the passed prop change value: function(newVal) { @@ -159,15 +207,6 @@ export default { } } }, - computed: { - // If a user passes in an error Message, add the error class to the input - classObject() { - return [ this.inputClass, this.errorMessage ? this.errorClass : ''] - }, - inlineText() { - return this.initialText || this.localValue - } - }, created() { // If created with editable = false then we are in inline-edit mode this.editable = !this.inline diff --git a/client/src/js/app/components/base-input-date.vue b/client/src/js/app/components/base-input-date.vue index 9398f59c7..3f710fc28 100644 --- a/client/src/js/app/components/base-input-date.vue +++ b/client/src/js/app/components/base-input-date.vue @@ -11,19 +11,24 @@ Slots include: --> @@ -112,17 +132,17 @@ export default { dialogOpened: false, } }, - watch: { - editable: function(value) { - if (value == false) this.showEditIcon = false - } - }, computed: { // If a user passes in an error Message, add the error class to the input classObject() { return [ this.inputClass, this.errorMessage ? this.errorClass : ''] } }, + watch: { + editable: function(value) { + if (value == false) this.showEditIcon = false + } + }, created: function() { // If created with inline then we are in inline-edit mode this.editable = !this.inline diff --git a/client/src/js/app/components/base-input-datetime.vue b/client/src/js/app/components/base-input-datetime.vue index 6e13c65a0..d93dcb02a 100644 --- a/client/src/js/app/components/base-input-datetime.vue +++ b/client/src/js/app/components/base-input-datetime.vue @@ -11,19 +11,24 @@ Slots include: --> @@ -113,17 +133,17 @@ export default { dialogOpened: false, } }, - watch: { - editable: function(value) { - if (value == false) this.showEditIcon = false - } - }, computed: { // If a user passes in an error Message, add the error class to the input classObject() { return [ this.inputClass, this.errorMessage ? this.errorClass : ''] } }, + watch: { + editable: function(value) { + if (value == false) this.showEditIcon = false + } + }, created: function() { // If created with inline then we are in inline-edit mode this.editable = !this.inline diff --git a/client/src/js/app/components/base-input-groupselect.vue b/client/src/js/app/components/base-input-groupselect.vue index 22b8b3ba0..499ba70d0 100644 --- a/client/src/js/app/components/base-input-groupselect.vue +++ b/client/src/js/app/components/base-input-groupselect.vue @@ -14,19 +14,24 @@ Slots include: --> @@ -262,6 +433,10 @@ export default { .select-selected { @apply tw-bg-white tw-h-8 tw-rounded tw-border tw-border-content-dark-background tw-flex tw-items-center tw-cursor-pointer } + +.select-selected-multiple { + @apply tw-bg-white tw-h-8 tw-rounded tw-border tw-border-content-dark-background tw-flex tw-items-center tw-cursor-pointer +} .select-selected.disabled { @apply tw-bg-content-page-background } @@ -312,6 +487,9 @@ export default { .items-list > div { @apply tw-py-2 tw-px-2 tw-cursor-pointer tw-text-black; } +.items-list-multiple > div { + @apply tw-py-2 tw-px-2 tw-cursor-pointer tw-text-black; +} /* Style items (options): */ .select-items { @apply tw-absolute tw-bg-white; @@ -336,10 +514,31 @@ export default { .items-list .item:hover, .same-as-selected { @apply tw-bg-content-active tw-text-white; } + +.items-list-multiple .item-multiple:hover { + @apply tw-bg-content-active tw-text-white; +} + +.item-multiple-selected { + @apply tw-bg-content-active tw-text-white; +} .select-search-input.small { @apply tw-h-8; } .select-search-input { - @apply tw-h-10; + @apply tw-h-12; +} + +.pill-input { + white-space: nowrap; + @apply tw-rounded-full tw-h-6 tw-max-w-xs tw-ml-1 tw-px-2 tw-py-1 tw-bg-content-active tw-text-sm +} + +::-webkit-scrollbar { + height: 2px; + width: 2px; + background-color: rgb(77, 118, 255); } - \ No newline at end of file + + + diff --git a/client/src/js/app/components/custom-dialog-box.vue b/client/src/js/app/components/custom-dialog-box.vue index 2fe5e8a1d..9ef1d27f5 100644 --- a/client/src/js/app/components/custom-dialog-box.vue +++ b/client/src/js/app/components/custom-dialog-box.vue @@ -1,9 +1,13 @@ + + + + diff --git a/client/src/js/app/components/extended-validation-provider.vue b/client/src/js/app/components/extended-validation-provider.vue index cd77eae3f..9a37e9146 100644 --- a/client/src/js/app/components/extended-validation-provider.vue +++ b/client/src/js/app/components/extended-validation-provider.vue @@ -1,15 +1,18 @@ @@ -17,7 +20,7 @@ import { ValidationProvider } from 'vee-validate'; export default { - name: 'extended-validation-provider', + name: 'ExtendedValidationProvider', components: { 'validation-provider': ValidationProvider }, diff --git a/client/src/js/app/components/filter-pills.vue b/client/src/js/app/components/filter-pills.vue index b13b29587..9e8461260 100644 --- a/client/src/js/app/components/filter-pills.vue +++ b/client/src/js/app/components/filter-pills.vue @@ -1,22 +1,23 @@ diff --git a/client/src/js/app/components/help-banner.vue b/client/src/js/app/components/help-banner.vue new file mode 100644 index 000000000..f2ab958d4 --- /dev/null +++ b/client/src/js/app/components/help-banner.vue @@ -0,0 +1,49 @@ + + + + + \ No newline at end of file diff --git a/client/src/js/app/components/herotitle.vue b/client/src/js/app/components/herotitle.vue index 44727a118..13e801d3b 100644 --- a/client/src/js/app/components/herotitle.vue +++ b/client/src/js/app/components/herotitle.vue @@ -1,13 +1,15 @@ - \ No newline at end of file +
+ diff --git a/client/src/js/app/components/motd.vue b/client/src/js/app/components/motd.vue index 25df33ce5..942e010d5 100644 --- a/client/src/js/app/components/motd.vue +++ b/client/src/js/app/components/motd.vue @@ -1,15 +1,27 @@ diff --git a/client/src/js/app/components/pagination.vue b/client/src/js/app/components/pagination.vue index a49316f44..bc49bca1b 100644 --- a/client/src/js/app/components/pagination.vue +++ b/client/src/js/app/components/pagination.vue @@ -8,24 +8,52 @@ Sends a page-changed event with page number and page size as payload Set totalRecords(0), initalPage (1), pageSizes([]) and number of page links (0..5) --> \ No newline at end of file diff --git a/client/src/js/app/components/pv-item.vue b/client/src/js/app/components/pv-item.vue index 23121b53e..1e36e5850 100644 --- a/client/src/js/app/components/pv-item.vue +++ b/client/src/js/app/components/pv-item.vue @@ -1,12 +1,20 @@ \ No newline at end of file diff --git a/client/src/js/app/components/search-mobile.vue b/client/src/js/app/components/search-mobile.vue index 060e68403..a0e8bc9b5 100644 --- a/client/src/js/app/components/search-mobile.vue +++ b/client/src/js/app/components/search-mobile.vue @@ -5,7 +5,11 @@ Used by datacollections view to move search and filter widgets to top of screen diff --git a/client/src/js/app/components/sidebar.vue b/client/src/js/app/components/sidebar.vue index e2f9afe03..7f6e0ae24 100644 --- a/client/src/js/app/components/sidebar.vue +++ b/client/src/js/app/components/sidebar.vue @@ -1,51 +1,94 @@ diff --git a/client/src/js/app/components/table.vue b/client/src/js/app/components/table.vue index f307b1eef..f8f134f0e 100644 --- a/client/src/js/app/components/table.vue +++ b/client/src/js/app/components/table.vue @@ -28,10 +28,16 @@ TODO - move relevant styles to this component style section
New LocationNew Local Contact
Full Name Lab / Company Name
Pickup Date
Comments
- + @click="$emit('sort-by', header.key)" + > + {{ header.title }} + + @@ -39,30 +45,47 @@ TODO - move relevant styles to this component style section - + + > + {{ getRowData(row, header) }} + - + - + - + -
{{header.title}}{{actions}} + {{ actions }} +
{{ getRowData(row, header) }} + +
{{noDataText}} + {{ noDataText }} +
@@ -94,12 +117,15 @@ export default { default: '' } }, - 'methods': { - 'getRowData': function(row, header) { + methods: { + getRowData: function(row, header) { const item = typeof row.get == 'undefined' ? row[header.key] : row.get(header.key) return typeof header.format == 'function' ? header.format(item) : item }, - }, + sortHeader(header) { + this.$emit('sort-by', header.key) + } + } } diff --git a/client/src/js/app/components/utils/collection-filters.vue b/client/src/js/app/components/utils/collection-filters.vue new file mode 100644 index 000000000..b4f6e0580 --- /dev/null +++ b/client/src/js/app/components/utils/collection-filters.vue @@ -0,0 +1,37 @@ + + + + + \ No newline at end of file diff --git a/client/src/js/app/index.js b/client/src/js/app/index.js index c68e0f0c2..d46aec355 100644 --- a/client/src/js/app/index.js +++ b/client/src/js/app/index.js @@ -1,5 +1,5 @@ -var Styles = require('css/main.scss') -var FontAwesome = require('font-awesome/css/font-awesome.css') +require('font-awesome/css/font-awesome.css') +require('css/main.scss') import Vue from 'vue' import VeeValidate from 'vee-validate' @@ -18,17 +18,15 @@ import VeeValidateCustomRules from 'app/mixins/vee-validate-custom-rules' Vue.use(VeeValidate) Vue.use(PortalVue) +Vue.use(VeeValidate) + Vue.config.productionTip = false Vue.config.devtools = !config.production - const vm = new Vue({ store, router, - render: function(h) { - if (config.maintenance) return h(MaintenanceView, {props: {'message': config.maintenance_message}}) - else return h(Main) - }, + mixins: [VeeValidateCustomRules], created: function() { console.log("VUE::created") @@ -37,7 +35,10 @@ const vm = new Vue({ application.start() }, - mixins: [VeeValidateCustomRules] + render: function(h) { + if (config.maintenance) return h(MaintenanceView, {props: {'message': config.maintenance_message}}) + else return h(Main) + } }).$mount('#synchweb-app') diff --git a/client/src/js/app/layouts/main.vue b/client/src/js/app/layouts/main.vue index 9cdae6ddd..e2e32a7b5 100644 --- a/client/src/js/app/layouts/main.vue +++ b/client/src/js/app/layouts/main.vue @@ -1,16 +1,19 @@ diff --git a/client/src/js/app/layouts/maintenance.vue b/client/src/js/app/layouts/maintenance.vue index 3f1b2f7b5..964b2e2fc 100644 --- a/client/src/js/app/layouts/maintenance.vue +++ b/client/src/js/app/layouts/maintenance.vue @@ -1,10 +1,18 @@