From 2915dacb27134d35de8b73898a98e97820b05932 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sun, 12 Sep 2021 12:27:06 -0700 Subject: [PATCH 01/58] Docker instruction (#91) * node 14, remove ssl key and cert * newer docker compose standard, default http * default http * http start * docker url and moer details * more docker instruction --- Dockerfile | 15 +++------------ README.md | 14 +++++++------- app.js | 10 +++++----- docker-compose.yml | 10 ++++++---- docker.md | 31 ++++++++++++++++++++++++++----- docker/api.json | 4 +--- docker/app.json | 2 -- docker/auth.json | 4 ++-- 8 files changed, 50 insertions(+), 40 deletions(-) diff --git a/Dockerfile b/Dockerfile index 340c7e16..1be18c56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12-alpine +FROM node:14-alpine RUN apk update && \ apk add openssl @@ -8,19 +8,10 @@ RUN apk add --no-cache tini ENTRYPOINT ["/sbin/tini", "--"] # web port EXPOSE 3001 -# api port -EXPOSE 3443 +# api port if https enabled +# EXPOSE 3443 COPY . /app -# create the certification file for ssl -WORKDIR /app/docker -RUN PASSWORD=$(openssl rand -hex 16) && \ - openssl genrsa -des3 -passout "pass:${PASSWORD}" -out server.pass.key 2048 && \ - openssl rsa -passin "pass:${PASSWORD}" -in server.pass.key -out server.key && \ - rm server.pass.key && \ - openssl req -new -key server.key -out server.csr \ - -subj "/C=US/ST=WA/L=Seattle/O=Traveler/OU=Dev/CN=example.com" && \ - openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt WORKDIR /app RUN npm install --only=prod RUN npm install -g nodemon@1.18.10 diff --git a/README.md b/README.md index 62cb830b..d7cdeace 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d ./etc/init.d/traveler-webapp start # Check progress of traveler-webapp ./etc/init.d/traveler-webapp status - + **Installing docker version (not for production use, but good for development and evaluation)** -1. centos docker install (centos) https://docs.docker.com/install/linux/docker-ce/centos/ +1. centos docker install (centos) https://docs.docker.com/install/linux/docker-ce/centos/ * uninstall old docker versions ``` sudo yum remove docker \ @@ -68,7 +68,7 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d ``` sudo systemctl enable docker ``` - * install docker-compose https://docs.docker.com/compose/install/ + * install docker-compose https://docs.docker.com/compose/install/ ``` sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose @@ -83,17 +83,17 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d sudo docker network create -d bridge --subnet 172.18.1.0/24 traveler-dev ``` 3. install dependencies - * traveler-mongo https://github.com/dongliu/traveler-mongo + * traveler-mongo https://github.com/dongliu/traveler-mongo * clone git repo ``` git clone https://github.com/dongliu/traveler-mongo.git (to e.g., ~/git/traveler) ``` * run traveler-mongo ``` - sudo docker-compose up + sudo docker-compose up ``` * access mongo express at http://localhost:8081. The username is traveler, and password is travelerpass. - * traveler-ldap https://github.com/dongliu/traveler-ldap + * traveler-ldap https://github.com/dongliu/traveler-ldap * clone git repo ``` git clone https://github.com/dongliu/traveler-ldap.git @@ -104,7 +104,7 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d ``` * test * You can check the state of the openladp service via the php ldap admin web https://localhost:6443. There is a default admin account that you can use to log in user name: cn=admin,dc=example,dc=org password: admin -4. install traveler https://github.com/AdvancedPhotonSource/traveler +4. install traveler https://github.com/AdvancedPhotonSource/traveler * clone git repo ``` git clone https://github.com/AdvancedPhotonSource/traveler.git diff --git a/app.js b/app.js index c3302cc7..8e34a853 100644 --- a/app.js +++ b/app.js @@ -190,16 +190,16 @@ app.get('/apis', function(req, res) { // Start application using settings var appPort = appSettings.app_port; var server; -if (appSettings.ssl_key !== undefined) { +if (appSettings.ssl_key) { var appCredentials = { key: fs.readFileSync('./' + configPath + '/' + appSettings.ssl_key), cert: fs.readFileSync('./' + configPath + '/' + appSettings.ssl_cert), }; server = https.createServer(appCredentials, app).listen(appPort, function() { - logger.info('Express server listening on ssl port ' + appPort); + logger.info('Express server listening on ssl port ' + app.get('port')); }); } else { - server = http.createServer(app).listen(app.get('port'), function() { + server = http.createServer(app).listen(appPort, function() { logger.info('Express server listening on port ' + app.get('port')); }); } @@ -221,7 +221,7 @@ require('./routes/api')(api); //Start Api using settings var apiserver; -if (apiSettings.ssl_key !== undefined) { +if (apiSettings.ssl_key) { var apiCredentials = { key: fs.readFileSync('./' + configPath + '/' + apiSettings.ssl_key), cert: fs.readFileSync('./' + configPath + '/' + apiSettings.ssl_cert), @@ -233,7 +233,7 @@ if (apiSettings.ssl_key !== undefined) { logger.info('API server listening on ssl port ' + api.get('port')); }); } else { - apiserver = http.createServer(api).listen(api.get('port'), function() { + apiserver = http.createServer(api).listen(apiPort, function() { logger.info('API server listening on port ' + api.get('port')); }); } diff --git a/docker-compose.yml b/docker-compose.yml index cdf2dea9..30cc3ec1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ version: "3" networks: default: - external: - name: traveler-dev + external: true + name: traveler-dev services: web: build: . @@ -10,10 +10,12 @@ services: - .:/app # exclude the node_modules - /app/node_modules - - /app/docker + # exclude the docker configuration + # - /app/docker ports: - "3001:3001" - - "3443:3443" + - "3002:3002" + # - "3443:3443" restart: unless-stopped command: nodemon app.js environment: diff --git a/docker.md b/docker.md index ec73c815..ff5630b0 100644 --- a/docker.md +++ b/docker.md @@ -2,13 +2,20 @@ ## install docker -See for instructions. +See for instructions. -You will alos need to install docker compose if it does not come with docker, see . +After installation finished, start the docker desktop, configure it for CPU, memory, and storage that you want it to use on your machine. + +Check if the docker desktop has `docker-compose` installed on your machine. If not, you need to install docker compose, see . ## create development network -The traveler application and its dependencies will be running in the same network named `traveler-dev`. Run the following to create the network. +Clone this repo to your local environment. Make sure you have the `traveler-dev` network in the docker. +``` +docker network list +``` + +If not, run the following in your console to create the network. ``` docker network create -d bridge --subnet 172.18.1.0/24 traveler-dev @@ -28,7 +35,9 @@ The traveler application can be run by running docker-compose up ``` -The traveler application can be accessed at +The traveler application can be accessed at + +If you want to server the application or the api on https, add `ssl_key` and `ssl_cert` configurations in the `app.json` and `api.json` files. You will need valid key and cert files in the `docker` directory, and set the values of `ssl_key` and `ssl_cert` to the name of the files. When you run for the first time, docker will build the image for you. If the application does not start successfully, try @@ -36,7 +45,9 @@ When you run for the first time, docker will build the image for you. If the app docker-compose --verbose up ``` -for detailed information. You should also check the logs of mongodb and open ldap service to see if there is an issue with those services. You can ssh into the running container with `docker exec -it traveler-aps_web_1 /bin/bash`, where `traveler-aps_web_1` is the running container name. +for detailed information. You should also check the logs of mongodb and open ldap service to see if there is an issue with those services. You can ssh into the running container with `docker exec -it traveler_web_1 /bin/sh`, where `traveler_web_1` is the running container name. + +`docker container list` shows all the containers running on your local. Run @@ -47,3 +58,13 @@ docker-compose down to stop the appplication. You can run with `docker-compose up -d` in a detached mode. Then you can run `docker-compose logs -f` to check the application log. + +## rebulid without cache + +``` +docker-compose build --no-cache +``` + +## clean the traveler docker image + +Run `docker image list` to see the images on your local. `docker image remove image_name` to clean the image from yoru local. diff --git a/docker/api.json b/docker/api.json index 929f98c7..e7ac1ac4 100644 --- a/docker/api.json +++ b/docker/api.json @@ -3,7 +3,5 @@ "api_read": "api_read_password", "api_write": "api_write_password" }, - "app_port" : "3443", - "ssl_key": "server.key", - "ssl_cert": "server.crt" + "app_port" : "3002" } diff --git a/docker/app.json b/docker/app.json index a6b2d14f..72c28ec6 100644 --- a/docker/app.json +++ b/docker/app.json @@ -1,8 +1,6 @@ { "org_name": "Lab", "app_port": "3001", - "ssl_key": "server.key", - "ssl_cert": "server.crt", "top_bar_urls": [ { "text": "Link", diff --git a/docker/auth.json b/docker/auth.json index 3b5e0f92..576d5d0d 100644 --- a/docker/auth.json +++ b/docker/auth.json @@ -1,7 +1,7 @@ { "type": "ldap", - "service": "https://localhost:3001", - "login_service": "https://localhost:3001/login", + "service": "http://localhost:3001", + "login_service": "http://localhost:3001/login", "default_roles": [ "read_all_forms", "write_public_travelers" From aaaeab6fa5c64276f2c3d94f8244baf24bd61c15 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Thu, 7 Oct 2021 09:22:22 -0700 Subject: [PATCH 02/58] Express update 87 (#95) * latest expressjs version * nvm * add helmet * install from package first, update nodemon * add body size config * default 1mb, no csp in helmet * copy lock --- .nvmrc | 1 + Dockerfile | 9 +- README.md | 71 +++--- app.js | 10 +- config/app_change.json | 1 + docker.md | 1 + docker/api.json | 8 +- docker/app.json | 1 + docker/auth.json | 5 +- package-lock.json | 366 +++++++++++++---------------- package.json | 3 +- public/javascripts/form-builder.js | 36 +-- routes/form-management.js | 65 +++-- routes/form.js | 50 ++-- 14 files changed, 303 insertions(+), 324 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..da2d3988 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 1be18c56..e0e6213a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,7 @@ FROM node:14-alpine +WORKDIR /app +COPY package.json . +COPY package-lock.json . RUN apk update && \ apk add openssl @@ -10,10 +13,8 @@ ENTRYPOINT ["/sbin/tini", "--"] EXPOSE 3001 # api port if https enabled # EXPOSE 3443 - -COPY . /app -WORKDIR /app RUN npm install --only=prod -RUN npm install -g nodemon@1.18.10 +RUN npm install -g nodemon@2 +COPY . . # RUN bower install CMD ["node", "app.js"] diff --git a/README.md b/README.md index d7cdeace..2e1e56ec 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d **Installing docker version (not for production use, but good for development and evaluation)** 1. centos docker install (centos) https://docs.docker.com/install/linux/docker-ce/centos/ - * uninstall old docker versions + - uninstall old docker versions ``` sudo yum remove docker \ docker-client \ @@ -49,7 +49,7 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d docker-engine-selinux \ docker-engine ``` - * install docker CE + - install docker CE ``` sudo yum install -y yum-utils \ device-mapper-persistent-data \ @@ -60,21 +60,21 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d sudo yum install docker-ce sudo systemctl start docker ``` - * test docker install + - test docker install ``` sudo docker run hello-world ``` - * configure docker to start on boot + - configure docker to start on boot ``` sudo systemctl enable docker ``` - * install docker-compose https://docs.docker.com/compose/install/ + - install docker-compose https://docs.docker.com/compose/install/ ``` sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose ``` - * test docker-compose + - test docker-compose ``` sudo docker-compose --version ``` @@ -83,40 +83,39 @@ For detailed deployment instructions please see https://confluence.aps.anl.gov/d sudo docker network create -d bridge --subnet 172.18.1.0/24 traveler-dev ``` 3. install dependencies - * traveler-mongo https://github.com/dongliu/traveler-mongo - * clone git repo - ``` - git clone https://github.com/dongliu/traveler-mongo.git (to e.g., ~/git/traveler) - ``` - * run traveler-mongo - ``` - sudo docker-compose up - ``` - * access mongo express at http://localhost:8081. The username is traveler, and password is travelerpass. - * traveler-ldap https://github.com/dongliu/traveler-ldap - * clone git repo - ``` - git clone https://github.com/dongliu/traveler-ldap.git - ``` - * run - ``` - sudo docker-compose up - ``` - * test - * You can check the state of the openladp service via the php ldap admin web https://localhost:6443. There is a default admin account that you can use to log in user name: cn=admin,dc=example,dc=org password: admin + - traveler-mongo https://github.com/dongliu/traveler-mongo + - clone git repo + ``` + git clone https://github.com/dongliu/traveler-mongo.git (to e.g., ~/git/traveler) + ``` + - run traveler-mongo + ``` + sudo docker-compose up + ``` + - access mongo express at http://localhost:8081. The username is traveler, and password is travelerpass. + - traveler-ldap https://github.com/dongliu/traveler-ldap + - clone git repo + ``` + git clone https://github.com/dongliu/traveler-ldap.git + ``` + - run + ``` + sudo docker-compose up + ``` + - test + - You can check the state of the openladp service via the php ldap admin web https://localhost:6443. There is a default admin account that you can use to log in user name: cn=admin,dc=example,dc=org password: admin 4. install traveler https://github.com/AdvancedPhotonSource/traveler - * clone git repo + - clone git repo ``` git clone https://github.com/AdvancedPhotonSource/traveler.git ``` - * to run and test - * must “sudo docker-compose up” traveler-mongo and traveler-ldap as described above first - ``` - sudo docker-compose up - ``` - * URL to run traveler is https://localhost:3001 - * find a user login in the ldap file traveler-ldap/seed/traveler.ldif - + - to run and test + - must “sudo docker-compose up” traveler-mongo and traveler-ldap as described above first + ``` + sudo docker-compose up + ``` + - URL to run traveler is https://localhost:3001 + - find a user login in the ldap file traveler-ldap/seed/traveler.ldif # Style and Lint diff --git a/app.js b/app.js index 8e34a853..f12165a0 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,7 @@ var config = require('./config/config.js'); config.load(); var express = require('express'); +const helmet = require('helmet'); var compression = require('compression'); var favicon = require('serve-favicon'); var morgan = require('morgan'); @@ -112,7 +113,11 @@ if (app.get('env') === 'production') { //TODO test //app.set('logger', logger); - +app.use( + helmet({ + contentSecurityPolicy: false, + }) +); app.use(compression()); app.use(express.static(path.join(__dirname, 'public'))); app.use(favicon(__dirname + '/public/favicon.ico')); @@ -141,7 +146,7 @@ app.use( }, }) ); -app.use(express.json()); +app.use(express.json({ limit: config.app.body_max_size || '1mb' })); app.use(express.urlencoded({ extended: true })); app.use(auth.proxied); app.use(auth.sessionLocals); @@ -209,6 +214,7 @@ var apiPort = apiSettings.app_port; api.enable('strict routing'); api.set('port', process.env.APIPORT || apiPort); +api.use(helmet()); api.use(morgan('common')); // api.use(express.logger({stream: access_logfile})); diff --git a/config/app_change.json b/config/app_change.json index 5c5bd5ef..80ccb715 100644 --- a/config/app_change.json +++ b/config/app_change.json @@ -15,6 +15,7 @@ "log_dir": "../logs/", "cookie_sec": "traveler", "cookie_life": 28800000, + "body_max_size": "1mb", "upload_size": 5, "default_form_public_access": -1, "default_traveler_public_access": 0, diff --git a/docker.md b/docker.md index ff5630b0..6c303d83 100644 --- a/docker.md +++ b/docker.md @@ -11,6 +11,7 @@ Check if the docker desktop has `docker-compose` installed on your machine. If n ## create development network Clone this repo to your local environment. Make sure you have the `traveler-dev` network in the docker. + ``` docker network list ``` diff --git a/docker/api.json b/docker/api.json index e7ac1ac4..317b9c8c 100644 --- a/docker/api.json +++ b/docker/api.json @@ -1,7 +1,7 @@ { - "api_users" : { - "api_read": "api_read_password", - "api_write": "api_write_password" + "api_users": { + "api_read": "api_read_password", + "api_write": "api_write_password" }, - "app_port" : "3002" + "app_port": "3002" } diff --git a/docker/app.json b/docker/app.json index 72c28ec6..e9d71fec 100644 --- a/docker/app.json +++ b/docker/app.json @@ -13,6 +13,7 @@ "log_dir": "../logs/", "cookie_sec": "traveler", "cookie_life": 28800000, + "body_max_size": "1mb", "upload_size": 5, "default_form_public_access": -1, "default_traveler_public_access": 0, diff --git a/docker/auth.json b/docker/auth.json index 576d5d0d..78587970 100644 --- a/docker/auth.json +++ b/docker/auth.json @@ -2,8 +2,5 @@ "type": "ldap", "service": "http://localhost:3001", "login_service": "http://localhost:3001/login", - "default_roles": [ - "read_all_forms", - "write_public_travelers" - ] + "default_roles": ["read_all_forms", "write_public_travelers"] } diff --git a/package-lock.json b/package-lock.json index 5933171a..ec7f880e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "traveler-aps", - "version": "1.4.2", + "version": "1.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -400,27 +400,22 @@ "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" }, "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "requires": { - "bytes": "3.0.0", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -428,19 +423,6 @@ "requires": { "ms": "2.0.0" } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" } } }, @@ -520,6 +502,11 @@ "readable-stream": "1.1.x" } }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, "callback-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz", @@ -1046,15 +1033,23 @@ } }, "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } }, "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==" }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, "cookie-parser": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", @@ -1076,6 +1071,11 @@ } } }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1825,52 +1825,42 @@ } }, "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "requires": { - "accepts": "~1.3.5", + "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.3.1", + "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.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.2", + "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "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" }, "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1879,55 +1869,10 @@ "ms": "2.0.0" } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, "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=" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "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==" - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } } } }, @@ -2106,16 +2051,16 @@ } }, "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", "unpipe": "~1.0.0" }, "dependencies": { @@ -2212,9 +2157,14 @@ } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs.realpath": { "version": "1.0.0", @@ -2449,6 +2399,11 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" + }, "help-me": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz", @@ -2501,14 +2456,15 @@ } }, "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=", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "requires": { "depd": "~1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, "http-signature": { @@ -2561,7 +2517,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -2710,9 +2665,9 @@ } }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "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==" }, "is-absolute": { "version": "1.0.0", @@ -3557,6 +3512,11 @@ } } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -3567,6 +3527,11 @@ "picomatch": "^2.0.5" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -4335,12 +4300,12 @@ } }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "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==", "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" } }, "psl": { @@ -4385,35 +4350,30 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "optional": true }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" }, + "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==" + }, "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } } }, "read-pkg": { @@ -4698,6 +4658,48 @@ "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", "dev": true }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "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==" + } + } + }, "serve-favicon": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", @@ -4728,65 +4730,20 @@ } }, "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "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==" - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } - } + "parseurl": "~1.3.3", + "send": "0.17.1" } }, "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shebang-command": { "version": "1.2.0", @@ -4989,9 +4946,9 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "stream-shift": { "version": "1.0.1", @@ -5327,6 +5284,11 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, "tough-cookie": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.2.tgz", diff --git a/package.json b/package.json index deccb83a..c431fb21 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,11 @@ "cookie-parser": "1.4.4", "debug": "^4.1.1", "errorhandler": "1.5.0", - "express": "4.16.4", + "express": "^4.17.1", "express-session": "1.15.6", "express-slash": "1.0.0", "file-stream-rotator": "0.0.5", + "helmet": "^4.6.0", "jade": "1.10.0", "jstransformer-marked": "1.0.1", "ldapjs": "1.0.0", diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index a1e5f4f1..5a673461 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -93,17 +93,21 @@ function archive_prior_released_forms(selected) { contentType: 'application/json', processData: false, }) - .done(function(data, textStatus, request) { - $('#message').append( - '
' + data + '
' - ); - }) - .fail(function(data, textStatus, request) { - $('#message').append( - '
' + data + '
' - ); - }) - .always(function() {}); + .done(function(data, textStatus, request) { + $('#message').append( + '
' + + data + + '
' + ); + }) + .fail(function(data, textStatus, request) { + $('#message').append( + '
' + + data + + '
' + ); + }) + .always(function() {}); } function userkey_error($userkey, msg) { @@ -1472,7 +1476,7 @@ function binding_events() { $('#modal .modal-body').empty(); let defaultTitle = $('#formtitle').text(); $('#modal .modal-body').append( - '
' ); @@ -1481,7 +1485,7 @@ function binding_events() { if (released_form_version_mgmt) { $('#modalLabel').html('Archive previously released form(s)'); $('#modal .modal-body').append( - '

Prior version(s) of this form:

' + '

Prior version(s) of this form:

' ); let priorVersionsColumns = [ selectColumn, @@ -1546,9 +1550,9 @@ function binding_events() { if (priorVersionsTable) { // get all selected forms let selected = fnGetSelectedInPage( - priorVersionsTable, - 'row-selected', - false + priorVersionsTable, + 'row-selected', + false ); let json = []; $(selected).each(function(s) { diff --git a/routes/form-management.js b/routes/form-management.js index 6e47598c..34b1d537 100644 --- a/routes/form-management.js +++ b/routes/form-management.js @@ -94,42 +94,41 @@ module.exports = function(app) { } ); - /** - * Batch archiving of multiple released forms - */ + /** + * Batch archiving of multiple released forms + */ app.put( - '/released-forms/archive', - auth.ensureAuthenticated, - auth.verifyRole('admin', 'manager'), - function updateStatus(req, res) { - let ids = req.body; - if (!ids || ids.length < 1) { - return res.status(400).send('no id(s) provided'); - } + '/released-forms/archive', + auth.ensureAuthenticated, + auth.verifyRole('admin', 'manager'), + function updateStatus(req, res) { + let ids = req.body; + if (!ids || ids.length < 1) { + return res.status(400).send('no id(s) provided'); + } - let stateTransition = require('../model/released-form').stateTransition; + let stateTransition = require('../model/released-form').stateTransition; - ids.forEach(function(id) { - ReleasedForm.findById({_id: id}, function (err, f) { - if (err) { - return res.status(400).send('Invalid id: ' + id); - } - f.status = 2; - f.archivedBy = req.session.userid; - f.archivedOn = Date.now(); - // check if we need to increment the version - // in this case, no - f.incrementVersion(); - f.saveWithHistory(req.session.userid) - .then(function () { - }) - .catch(function (err) { - return res.status(500).send('Failed to archive form: ' + id); - }); - }); - }); - return res.status(200).send('Archived form(s): ' + ids.join(', ')); - } + ids.forEach(function(id) { + ReleasedForm.findById({ _id: id }, function(err, f) { + if (err) { + return res.status(400).send('Invalid id: ' + id); + } + f.status = 2; + f.archivedBy = req.session.userid; + f.archivedOn = Date.now(); + // check if we need to increment the version + // in this case, no + f.incrementVersion(); + f.saveWithHistory(req.session.userid) + .then(function() {}) + .catch(function(err) { + return res.status(500).send('Failed to archive form: ' + id); + }); + }); + }); + return res.status(200).send('Archived form(s): ' + ids.join(', ')); + } ); app.put( diff --git a/routes/form.js b/routes/form.js index e399e50c..4d621d7b 100644 --- a/routes/form.js +++ b/routes/form.js @@ -296,26 +296,34 @@ module.exports = function(app) { ); app.get( - '/forms/:id/released/json', - auth.ensureAuthenticated, - reqUtils.exist('id', Form), - reqUtils.canReadMw('id'), - function(req, res) { - try { - ReleasedForm.find({ - 'base._id': req.params.id, - status: 1, // released - }, function (err, existingForms) { - if (err) { - return res.status(500).send(error.message); - } - debug('found ' + existingForms.length + ' previously released form(s) based on : ' + req.params.id); - return res.status(200).json(existingForms); - }); - } catch (error) { + '/forms/:id/released/json', + auth.ensureAuthenticated, + reqUtils.exist('id', Form), + reqUtils.canReadMw('id'), + function(req, res) { + try { + ReleasedForm.find( + { + 'base._id': req.params.id, + status: 1, // released + }, + function(err, existingForms) { + if (err) { return res.status(500).send(error.message); + } + debug( + 'found ' + + existingForms.length + + ' previously released form(s) based on : ' + + req.params.id + ); + return res.status(200).json(existingForms); } + ); + } catch (error) { + return res.status(500).send(error.message); } + } ); app.post( @@ -879,9 +887,7 @@ module.exports = function(app) { return res .status(400) .send( - `A form with same title, type, and version was already released in ${ - existingForm._id - }.` + `A form with same title, type, and version was already released in ${existingForm._id}.` ); } } catch (error) { @@ -891,8 +897,8 @@ module.exports = function(app) { try { const saveForm = await new ReleasedForm(releasedForm).save(); const url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - `/released-forms/${saveForm._id}/`; + (req.proxied ? authConfig.proxied_service : authConfig.service) + + `/released-forms/${saveForm._id}/`; form.save(); return res.status(201).json({ location: url, From 3cfb0331af5f591534f47bb265b6c3a3bece4016 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Thu, 7 Oct 2021 09:24:05 -0700 Subject: [PATCH 03/58] merge APS release 1.5.1 (#96) * Resolve undefined exception for loading form details. Closes #303 * Prepare release 1.5.1 Co-authored-by: Dariusz --- lib/req-utils.js | 2 +- package.json | 4 ++-- public/release.txt | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/req-utils.js b/lib/req-utils.js index cb9b8402..6add5736 100644 --- a/lib/req-utils.js +++ b/lib/req-utils.js @@ -282,7 +282,7 @@ function getAccess(req, doc) { return doc.sharedWith.id(req.session.userid).access; } var i; - if (doc.sharedGroup) { + if (doc.sharedGroup && req.session.memberOf) { for (i = 0; i < req.session.memberOf.length; i += 1) { if ( doc.sharedGroup.id(req.session.memberOf[i]) && diff --git a/package.json b/package.json index c431fb21..2ec8970f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "traveler-aps", - "version": "1.5.0", + "version": "1.5.1", "private": true, "scripts": { "start": "node app" @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/AdvancedPhotonSource/traveler.git", - "release": "APS-1.5.0" + "release": "APS-1.5.1" }, "dependencies": { "basic-auth": "1.0.0", diff --git a/public/release.txt b/public/release.txt index 22a9ef64..f33df33c 100644 --- a/public/release.txt +++ b/public/release.txt @@ -1,3 +1,6 @@ +APS-1.5.1 +- Resolve issue with undefined exception when loading form for some users. + APS-1.5.0 # Features - MQTT integration with eTraveler From 86ca07102cabdce6f260e7d18040d401ce955a80 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 9 Nov 2021 06:56:57 -0800 Subject: [PATCH 04/58] anon holder (#98) --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 066ef7f8..de682fe8 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Dong Liu, Dariusz Jarosz +Copyright (c) 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 35d0035565f6814f576ff96913f80ddaaacf930f Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 30 Nov 2021 10:48:10 -0800 Subject: [PATCH 05/58] Form approval 89 (#97) * add feature flag * clean comment * start model readme doc * repurpose form status * update state transition * js format not used any more * add spaces back * use airbnb base, update eslint * newer eslint * review model * relax define and use * update lint-staged to stop commit when eslint errors * add forms under reviews table to user form view * update draft and submitted form json * add submitted form tab and table * prettier in lint staged * add review column * monitor jade change * add review route and view * add review get route * reviewer request typeahead and table * reviwer added * more info on review page, transform review result * reviewer sees the review action * group buttons * allow reviewer to access, eslint fix * form is editable while under review * map sAMAccountName * eslint and logger * add review request in model * update view * add review request * add request by and on columns * start remove * remove fixed * eslint and logger * update is reviewer * admin only * add review nav, route and view * inject user id * review route and ui review list * request on * add review accordion * review result add and display * default out * review result to string * all approve method * release when all approved, keep submitted request * debug to dev dep, add color * force color * debug back to dep, audit fix * fix and check __review * add version in review result * release forms in a page * link to released forms page * remove releaesd form tabs, add closed forms * form status * closed form route * close request after release, close fix, show reviews in closed form * find released from the form and form direct parent * allow released by user to archive a released form * fix route * more info when update status, no redirect, archive forms * rework in view * close review requests on the first rework * rework set the doc to draft with review results --- .eslintignore | 5 + .eslintrc | 11 +- app.js | 131 +- docker-compose.yml | 1 + docker/ad.json | 3 +- docker/app.json | 3 +- jsformat.json | 20 - lib/ldap-client.js | 98 +- lib/req-utils.js | 148 +- lib/review.js | 59 + model/README.md | 40 + model/binder.js | 65 +- model/form.js | 98 +- model/history.js | 1 - model/released-form.js | 12 +- model/review.js | 198 ++ model/user.js | 25 +- package-lock.json | 3431 +++++++++++++------------ package.json | 29 +- public/javascripts/form-builder.js | 717 +++--- public/javascripts/form-management.js | 78 +- public/javascripts/forms.js | 206 +- public/javascripts/released-forms.js | 312 +++ public/javascripts/review.js | 221 ++ public/javascripts/reviews.js | 46 + public/javascripts/share.js | 140 +- public/javascripts/table.js | 1026 ++++---- routes/form-management.js | 133 +- routes/form.js | 752 +++--- routes/review.js | 57 + views/form-builder.jade | 81 +- views/form-management.jade | 2 +- views/forms.jade | 27 +- views/layout.jade | 9 +- views/released-forms.jade | 76 + views/review.jade | 53 + views/reviews.jade | 53 + 37 files changed, 4833 insertions(+), 3534 deletions(-) create mode 100644 .eslintignore delete mode 100644 jsformat.json create mode 100644 lib/review.js create mode 100644 model/README.md create mode 100644 model/review.js create mode 100644 public/javascripts/released-forms.js create mode 100644 public/javascripts/review.js create mode 100644 public/javascripts/reviews.js create mode 100644 routes/review.js create mode 100644 views/released-forms.jade create mode 100644 views/review.jade create mode 100644 views/reviews.jade diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..b6090d8b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +etc +sbin +!public +public/* +public/javascripts \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index f80076ff..459d4d6d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,8 @@ "parserOptions": { "ecmaVersion": 2017 }, - "extends": ["eslint:recommended", "prettier"], + + "extends": ["airbnb-base", "prettier"], "rules": { "no-console": 0, "quotes": [2, "single", "avoid-escape"], @@ -21,6 +22,12 @@ "max-params": [2, 10], "operator-assignment": 0, "consistent-this": 0, - "no-unused-expressions": 1 + "no-unused-expressions": 1, + "no-underscore-dangle": 1, + "no-use-before-define": 1, + "no-unused-vars": 1, + "no-param-reassign": 1, + "no-prototype-builtins": 1, + "no-continue": 1 } } diff --git a/app.js b/app.js index f12165a0..db949203 100644 --- a/app.js +++ b/app.js @@ -1,52 +1,48 @@ /* jslint es5: true */ - -var config = require('./config/config.js'); -config.load(); - -var express = require('express'); +const express = require('express'); const helmet = require('helmet'); -var compression = require('compression'); -var favicon = require('serve-favicon'); -var morgan = require('morgan'); -var methodOverride = require('method-override'); -var expressSession = require('express-session'); -var cookieParser = require('cookie-parser'); -var errorHandler = require('errorhandler'); - -var http = require('http'); -var https = require('https'); -var fs = require('fs'); -var multer = require('multer'); -var path = require('path'); - +const compression = require('compression'); +const favicon = require('serve-favicon'); +const morgan = require('morgan'); +const methodOverride = require('method-override'); +const expressSession = require('express-session'); +const cookieParser = require('cookie-parser'); +const errorHandler = require('errorhandler'); + +const http = require('http'); +const https = require('https'); +const fs = require('fs'); +const multer = require('multer'); +const path = require('path'); +const rotator = require('file-stream-rotator'); + +const mongoose = require('mongoose'); const logger = require('./lib/loggers').getLogger(); +const config = require('./config/config'); -var rotator = require('file-stream-rotator'); - -var mongoose = require('mongoose'); - -var configPath = config.configPath; -var apiSettings = config.api; -var mongoConfig = config.mongo; -var appSettings = config.app; +config.load(); +const { configPath } = config; +const apiSettings = config.api; +const mongoConfig = config.mongo; +const appSettings = config.app; // Load MongoDB object modeling tool mongoose.connection.close(); // Load MongoDB models -require('./model/user.js'); -require('./model/form.js'); -require('./model/released-form.js'); -require('./model/traveler.js'); -require('./model/binder.js'); -require('./model/history.js'); - -//Connect to mongo database -var mongoAddress = 'mongodb://'; +require('./model/user'); +require('./model/form'); +require('./model/released-form'); +require('./model/traveler'); +require('./model/binder'); +require('./model/history'); + +// Connect to mongo database +let mongoAddress = 'mongodb://'; mongoAddress += mongoConfig.server_address || 'localhost'; -mongoAddress += ':' + (mongoConfig.server_port || '27017'); -mongoAddress += '/' + (mongoConfig.traveler_db || 'traveler'); +mongoAddress += `:${mongoConfig.server_port || '27017'}`; +mongoAddress += `/${mongoConfig.traveler_db || 'traveler'}`; -var mongoOptions = { +const mongoOptions = { native_parser: true, poolSize: 5, connectTimeoutMS: 30000, @@ -68,7 +64,7 @@ mongoose.connection.on('connected', function() { }); mongoose.connection.on('error', function(err) { - logger.info('Mongoose default connection error: ' + err); + logger.info(`Mongoose default connection error: ${err}`); }); mongoose.connection.on('disconnected', function() { @@ -76,32 +72,34 @@ mongoose.connection.on('disconnected', function() { }); // LDAP client -var adClient = require('./lib/ldap-client'); +const adClient = require('./lib/ldap-client'); + adClient.getDefaultClient(function(client, ldapClientCleanup) { logger.info('ldap connection successfully tested'); ldapClientCleanup(); }); // CAS client -var auth = require('./lib/auth'); +const auth = require('./lib/auth'); // api and web app -var api = express(); -var app = express(); +const api = express(); +const app = express(); /* Configure Web Application */ app.locals.orgName = appSettings.org_name; +let access_logfile; app.enable('strict routing'); if (app.get('env') === 'production') { - var access_logfile = rotator.getStream({ + access_logfile = rotator.getStream({ filename: path.resolve(appSettings.log_dir, 'access.log'), frequency: 'daily', }); } app.set('port', process.env.PORT || appSettings.app_port); -app.set('views', __dirname + '/views'); +app.set('views', `${__dirname}/views`); app.set('view engine', 'jade'); if (app.get('env') === 'production') { app.use( @@ -111,8 +109,6 @@ if (app.get('env') === 'production') { ); } -//TODO test -//app.set('logger', logger); app.use( helmet({ contentSecurityPolicy: false, @@ -120,7 +116,7 @@ app.use( ); app.use(compression()); app.use(express.static(path.join(__dirname, 'public'))); -app.use(favicon(__dirname + '/public/favicon.ico')); +app.use(favicon(`${__dirname}/public/favicon.ico`)); if (app.get('env') === 'development') { app.use(morgan('common')); } @@ -155,7 +151,7 @@ if (app.get('env') === 'development') { app.use(errorHandler()); } -var routes = require('./routes'); +const routes = require('./routes'); require('./routes/main')(app); require('./routes/form')(app); @@ -164,6 +160,7 @@ require('./routes/traveler')(app); require('./routes/binder')(app); require('./routes/report')(app); require('./routes/admin')(app); +require('./routes/review')(app); require('./routes/user')(app); require('./routes/group')(app); require('./routes/profile')(app); @@ -184,33 +181,33 @@ app.get('/login', auth.ensureAuthenticated, function(req, res) { } // something wrong - res.send(400, 'please enable cookie in your browser'); + return res.send(400, 'please enable cookie in your browser'); }); app.get('/logout', routes.logout); app.get('/apis', function(req, res) { - res.redirect('https://' + req.host + ':' + api.get('port') + req.originalUrl); + res.redirect(`https://${req.host}:${api.get('port')}${req.originalUrl}`); }); // Start application using settings -var appPort = appSettings.app_port; -var server; +const appPort = appSettings.app_port; +let server; if (appSettings.ssl_key) { - var appCredentials = { - key: fs.readFileSync('./' + configPath + '/' + appSettings.ssl_key), - cert: fs.readFileSync('./' + configPath + '/' + appSettings.ssl_cert), + const appCredentials = { + key: fs.readFileSync(`./${configPath}/${appSettings.ssl_key}`), + cert: fs.readFileSync(`./${configPath}/${appSettings.ssl_cert}`), }; server = https.createServer(appCredentials, app).listen(appPort, function() { - logger.info('Express server listening on ssl port ' + app.get('port')); + logger.info(`Express server listening on ssl port ${app.get('port')}`); }); } else { server = http.createServer(app).listen(appPort, function() { - logger.info('Express server listening on port ' + app.get('port')); + logger.info(`Express server listening on port ${app.get('port')}`); }); } /* Configure REST API */ -var apiPort = apiSettings.app_port; +const apiPort = apiSettings.app_port; api.enable('strict routing'); api.set('port', process.env.APIPORT || apiPort); @@ -225,22 +222,22 @@ api.use(compression()); require('./routes/api')(api); -//Start Api using settings -var apiserver; +// Start Api using settings +let apiserver; if (apiSettings.ssl_key) { - var apiCredentials = { - key: fs.readFileSync('./' + configPath + '/' + apiSettings.ssl_key), - cert: fs.readFileSync('./' + configPath + '/' + apiSettings.ssl_cert), + const apiCredentials = { + key: fs.readFileSync(`./${configPath}/${apiSettings.ssl_key}`), + cert: fs.readFileSync(`./${configPath}/${apiSettings.ssl_cert}`), }; apiserver = https .createServer(apiCredentials, api) .listen(api.get('port'), function() { - logger.info('API server listening on ssl port ' + api.get('port')); + logger.info(`API server listening on ssl port ${api.get('port')}`); }); } else { apiserver = http.createServer(api).listen(apiPort, function() { - logger.info('API server listening on port ' + api.get('port')); + logger.info(`API server listening on port ${api.get('port')}`); }); } diff --git a/docker-compose.yml b/docker-compose.yml index 30cc3ec1..0f71a06a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,3 +21,4 @@ services: environment: - TRAVELER_CONFIG_REL_PATH=docker - DEBUG=traveler:* + - DEBUG_COLORS=true diff --git a/docker/ad.json b/docker/ad.json index 039673d9..58bf45db 100644 --- a/docker/ad.json +++ b/docker/ad.json @@ -18,7 +18,8 @@ "company": "o", "thumbnailPhoto": "jpegPhoto", "uid": "uid", - "telephoneNumber": "homephone" + "telephoneNumber": "homephone", + "sAMAccountName": "uid" }, "shareUsers": true, diff --git a/docker/app.json b/docker/app.json index e9d71fec..fa7d9993 100644 --- a/docker/app.json +++ b/docker/app.json @@ -17,5 +17,6 @@ "upload_size": 5, "default_form_public_access": -1, "default_traveler_public_access": 0, - "default_binder_public_access": 0 + "default_binder_public_access": 0, + "released_form_version_mgmt": 0 } diff --git a/jsformat.json b/jsformat.json deleted file mode 100644 index e914b42b..00000000 --- a/jsformat.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - // exposed jsbeautifier options - "indent_with_tabs": false, - "preserve_newlines": true, - "max_preserve_newlines": 4, - "space_in_paren": false, - "jslint_happy": true, - "brace_style": "collapse", - "keep_array_indentation": false, - "keep_function_indentation": false, - "eval_code": false, - "unescape_strings": false, - "break_chained_methods": false, - "e4x": false, - "wrap_line_length": 0, - - // jsformat options - "format_on_save": false, - "jsbeautifyrc_files": false -} diff --git a/lib/ldap-client.js b/lib/ldap-client.js index 42e7c5f9..702f71cb 100644 --- a/lib/ldap-client.js +++ b/lib/ldap-client.js @@ -1,10 +1,15 @@ -var ldap = require('ldapjs'); -var config = require('../config/config.js'); -var configPath = config.configPath; +const ldap = require('ldapjs'); +const fs = require('fs'); +const util = require('util'); +const debug = require('debug')('traveler:ldap-client'); +const config = require('../config/config'); +const logger = require('./loggers').getLogger(); -var ad = config.ad; +const { configPath } = config; -var clientOptions = { +const { ad } = config; + +const clientOptions = { url: ad.url, maxConnections: 5, connectTimeout: 10 * 1000, @@ -13,9 +18,8 @@ var clientOptions = { bindCredentials: ad.adminPassword, }; if (ad.ldapsCA !== undefined) { - var fs = require('fs'); clientOptions.tlsOptions = { - ca: fs.readFileSync(configPath + '/' + ad.ldapsCA), + ca: fs.readFileSync(`${configPath}/${ad.ldapsCA}`), rejectUnauthorized: ad.ldapsRejectUnauthorized, }; } @@ -25,7 +29,7 @@ function getDefaultClient(cb) { } function getClient(clientOpts, cb) { - var client = ldap.createClient(clientOpts); + const client = ldap.createClient(clientOpts); client.on('connect', function() { cb(client, function cleanUp() { client.unbind(); @@ -33,16 +37,16 @@ function getClient(clientOpts, cb) { }); }); client.on('timeout', function(message) { - console.error(message); + logger.error(message); }); client.on('error', function(error) { - console.error(error); + logger.error(error); }); } function searchForUser(username, cb) { - var searchFilter = ad.searchFilter.replace('_id', username); - var opts = { + const searchFilter = ad.searchFilter.replace('_id', username); + const opts = { filter: searchFilter, scope: 'sub', }; @@ -52,32 +56,33 @@ function searchForUser(username, cb) { if (err) { return cb(err); } - var results = []; + const results = []; result.on('searchEntry', function(entry) { results.push(entry.object); }); - result.on('error', function(err) { - console.log(JSON.stringify(err)); + result.on('error', function(error) { + logger.error(JSON.stringify(error)); cleanUp(); - return cb(err); + return cb(error); }); - result.on('end', function(result) { + result.on('end', function() { cleanUp(); switch (results.length) { case 0: - cb('No result for specified username: ' + username); + cb(`No result for specified username: ${username}`); break; case 1: - mapDefaultKeyNames(results, ad.defaultKeys, function(err, items) { + mapDefaultKeyNames(results, ad.defaultKeys, function(error, items) { cb(null, items[0]); }); break; default: - cb('Non unique for specified username: ' + username); + cb(`Non unique for specified username: ${username}`); break; } }); + return null; }); }); } @@ -86,11 +91,11 @@ function search(base, opts, raw, cb) { getDefaultClient(function(client, cleanUp) { client.search(base, opts, function(err, result) { if (err) { - console.log(JSON.stringify(err)); + logger.error(JSON.stringify(err)); cleanUp(); return cb(err); } - var items = []; + const items = []; result.on('searchEntry', function(entry) { if (raw) { items.push(entry.raw); @@ -98,17 +103,17 @@ function search(base, opts, raw, cb) { items.push(entry.object); } }); - result.on('error', function(err) { - console.log(JSON.stringify(err)); + result.on('error', function(error) { + logger.error(JSON.stringify(error)); cleanUp(); - return cb(err); + return cb(error); }); - result.on('end', function(result) { + result.on('end', function(r) { cleanUp(); - if (result.status !== 0) { - var err = 'non-zero status from LDAP search: ' + result.status; - console.log(JSON.stringify(err)); - return cb(err); + if (r.status !== 0) { + const error = `non-zero status from LDAP search: ${r.status}`; + logger.error(JSON.stringify(error)); + return cb(error); } switch (items.length) { case 0: @@ -116,29 +121,34 @@ function search(base, opts, raw, cb) { default: mapDefaultKeyNames(items, ad.defaultKeys, cb); } + return null; }); + return null; }); }); } -function mapDefaultKeyNames(items, defaultKeys, cb) { - for (var idx in items) { - var item = items[idx]; - for (var key in defaultKeys) { - if (item[defaultKeys[key]] !== undefined) { - item[key] = item[defaultKeys[key]]; +function mapDefaultKeyNames(items, defaultItems, cb) { + debug(`before map: ${util.inspect(items)}`); + const itemKeys = Object.keys(items); + const defaultKeys = Object.keys(defaultItems); + itemKeys.forEach(itemKey => { + const item = items[itemKey]; + defaultKeys.forEach(defaultKey => { + if (item[defaultItems[defaultKey]] !== undefined) { + item[defaultKey] = item[defaultItems[defaultKey]]; } else { - item[key] = ''; + item[defaultKey] = ''; } - } - } - + }); + }); + debug(`after map: ${util.inspect(items)}`); return cb(null, items); } module.exports = { - getDefaultClient: getDefaultClient, - getClient: getClient, - search: search, - searchForUser: searchForUser, + getDefaultClient, + getClient, + search, + searchForUser, }; diff --git a/lib/req-utils.js b/lib/req-utils.js index 6add5736..905df7be 100644 --- a/lib/req-utils.js +++ b/lib/req-utils.js @@ -1,9 +1,9 @@ -var sanitize = require('sanitize-html'); -var _ = require('lodash'); -var routesUtilities = require('../utilities/routes.js'); +const sanitize = require('sanitize-html'); +const _ = require('lodash'); const debug = require('debug')('traveler:req-utils'); +const routesUtilities = require('../utilities/routes'); -var htmlSanitizeConfiguration = { +const htmlSanitizeConfiguration = { allowedTags: [ 'div', 'a', @@ -68,26 +68,23 @@ var htmlSanitizeConfiguration = { */ function filter(list, names) { return function(req, res, next) { - var k; - var found = false; - for (k in req[list]) { - if (req[list].hasOwnProperty(k)) { - if (names.indexOf(k) !== -1) { - found = true; - } else { - req[list][k] = null; - } + let found = false; + const keys = Object.keys(req[list]); + keys.forEach(key => { + if (names.includes(key)) { + found = true; + } else { + req[list][key] = null; } - } + }); if (found) { - next(); - } else { - return res.status(400).send(`cannot find any of ${names} in ${list}`); + return next(); } + return res.status(400).send(`cannot find any of ${names} in ${list}`); }; } -var sanitizeValue = function(val) { +const sanitizeValue = function(val) { if (_.isString(val)) { return sanitizeText(val); } @@ -157,8 +154,8 @@ function sanitizeMw(list, names) { */ function hasAll(list, names) { return function(req, res, next) { - var i; - var miss = false; + let i; + let miss = false; let missed; for (i = 0; i < names.length; i += 1) { if (!req[list].hasOwnProperty(names[i])) { @@ -170,7 +167,7 @@ function hasAll(list, names) { if (miss) { return res.status(400).send(`cannot find ${missed} in ${list}`); } - next(); + return next(); }; } @@ -192,13 +189,11 @@ function existSource(pName, source, collection) { } if (!item) { - return res - .status(404) - .send('item ' + req[source][pName] + ' not found'); + return res.status(404).send(`item ${req[source][pName]} not found`); } req[req[source][pName]] = item; - next(); + return next(); }); }; } @@ -221,18 +216,15 @@ function exist(pName, collection) { */ function status(pName, sList) { return function(req, res, next) { - var s = req[req.params[pName]].status; + const s = req[req.params[pName]].status; if (sList.indexOf(s) === -1) { return res .status(400) .send( - 'request is not allowed for item ' + - req.params[pName] + - ' status ' + - s + `request is not allowed for item ${req.params[pName]} status ${s}` ); } - next(); + return next(); }; } @@ -244,26 +236,23 @@ function status(pName, sList) { */ function archived(pName, a) { return function(req, res, next) { - var arch = req[req.params[pName]].archived; + const arch = req[req.params[pName]].archived; if (a !== arch) { return res .status(400) .send( - 'request is not allowed for item ' + - req.params[pName] + - ' archived ' + - arch + `request is not allowed for item ${req.params[pName]} archived ${arch}` ); } - next(); + return next(); }; } -/***** +/** *** access := -1 // no access | 0 // read | 1 // write -*****/ +**** */ function getAccess(req, doc) { if (doc.publicAccess === 1) { @@ -278,10 +267,17 @@ function getAccess(req, doc) { if (routesUtilities.checkUserRole(req, 'admin')) { return 1; } + if ( + doc.__review && + doc.__review.reviewRequests && + doc.__review.reviewRequests.id(req.session.userid) + ) { + return 1; + } if (doc.sharedWith && doc.sharedWith.id(req.session.userid)) { return doc.sharedWith.id(req.session.userid).access; } - var i; + let i; if (doc.sharedGroup && req.session.memberOf) { for (i = 0; i < req.session.memberOf.length; i += 1) { if ( @@ -322,7 +318,7 @@ function canWriteMw(pName) { .status(403) .send('you are not authorized to access this resource'); } - next(); + return next(); }; } @@ -343,12 +339,16 @@ function canReadMw(pName, source = 'params') { .status(403) .send('you are not authorized to access this resource'); } - next(); + return next(); }; } function isOwner(req, doc) { - if (doc.createdBy === req.session.userid && !doc.owner) { + if ( + (doc.createdBy === req.session.userid || + doc.releasedBy === req.session.userid) && + !doc.owner + ) { return true; } if (doc.owner === req.session.userid) { @@ -369,7 +369,26 @@ function isOwnerMw(pName) { .status(403) .send('you are not authorized to access this resource'); } - next(); + return next(); + }; +} + +/** + * check if the user is the owner of the document, or is admin, if yes next() + * @param {String} pName the object's id to check + * @return {Function} the middleware + */ +function isOwnerOrAdminMw(pName) { + return function(req, res, next) { + if ( + isOwner(req, req[req.params[pName]]) || + routesUtilities.checkUserRole(req, 'admin') + ) { + return next(); + } + return res + .status(403) + .send('you are not authorized to access this resource'); }; } @@ -387,12 +406,12 @@ function requireOwner(condition, pName) { .status(403) .send('you are not authorized to access this resource'); } - next(); + return next(); }; } function getSharedWith(sharedWith, name) { - var i; + let i; if (sharedWith.length === 0) { return -1; } @@ -405,7 +424,7 @@ function getSharedWith(sharedWith, name) { } function getSharedGroup(sharedGroup, id) { - var i; + let i; if (sharedGroup.length === 0) { return -1; } @@ -418,22 +437,23 @@ function getSharedGroup(sharedGroup, id) { } module.exports = { - filter: filter, - hasAll: hasAll, + filter, + hasAll, sanitize: sanitizeMw, - sanitizeText: sanitizeText, - exist: exist, - existSource: existSource, - status: status, - archived: archived, - canRead: canRead, - canReadMw: canReadMw, - canWrite: canWrite, - canWriteMw: canWriteMw, - isOwner: isOwner, - isOwnerMw: isOwnerMw, - getAccess: getAccess, - getSharedWith: getSharedWith, - getSharedGroup: getSharedGroup, - requireOwner: requireOwner, + sanitizeText, + exist, + existSource, + status, + archived, + canRead, + canReadMw, + canWrite, + canWriteMw, + isOwner, + isOwnerMw, + isOwnerOrAdminMw, + getAccess, + getSharedWith, + getSharedGroup, + requireOwner, }; diff --git a/lib/review.js b/lib/review.js new file mode 100644 index 00000000..23fa66f1 --- /dev/null +++ b/lib/review.js @@ -0,0 +1,59 @@ +const debug = require('debug')('traveler:lib:review'); +const mongoose = require('mongoose'); + +const User = mongoose.model('User'); +const logger = require('./loggers').getLogger(); + +async function addReviewRequest(req, res, doc) { + const { name } = req.body; + try { + const reviewer = await User.findOne({ + name, + }).exec(); + if (!reviewer) { + return res + .status(400) + .send( + `please add ${name} to etraveler user list and assign manager role to the user.` + ); + } + if (!reviewer.roles.includes('manager')) { + return res + .status(400) + .send(`User ${name} needs to have manager role in order to review.`); + } + await doc.requestReview(req.session.userid, reviewer); + return res.status(201).send(`review request added for user ${name} .`); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } +} + +async function removeReviewRequest(req, res, doc) { + const { requestId } = req.params; + const ids = requestId.split(','); + try { + logger.info(`review request of ${ids} removed from ${doc._id}`); + await doc.removeReviewRequest(ids[0]); + return res.status(200).json(ids); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } +} + +async function addReviewResult(req, res, doc) { + const { result = '2', comment, v } = req.body; + try { + await doc.addReviewResult(req.session.userid, result, comment, v); + return res + .status(201) + .send(`review result from user ${req.session.userid} added.`); + } catch (error) { + logger.error(`failed to add review result, ${error}`); + return res.status(500).send(error.message); + } +} + +module.exports = { addReviewRequest, removeReviewRequest, addReviewResult }; diff --git a/model/README.md b/model/README.md new file mode 100644 index 00000000..605b0c3e --- /dev/null +++ b/model/README.md @@ -0,0 +1,40 @@ +# Models + +There are two types of models, those for entities, and others for attachable features of entities. + +## Entity models + +### form + +The forms are the templates on the basis of which the released forms are created. There are two type of forms, normal and discrepancy. The discrepancy forms are only for collection of discrepancy data. + +State transition of form + +archived <-------------------------------\ + ^ | | +draft ----> submitted for review ----> closed +^-----------------| + +### released form + +The released forms are the forms that have been approved for release, and therefore can be used to create travelers. A released form has two components, the base form and an optional discrepancy form. There are three possible combinations of base and discrepancy: base only, discrepancy only, base plus discrepancy. Note that only previously released discrepancy forms can be used to create a base + discrepancy. + +### traveler + +### binder + +### user + +## Attachable feature models + +## share + +## history + +## review + +An approval model represents the request and the result of a reviewing process. It contains + +- policy: all | majority | any +- requested reviewers: admin or manager role required +- review results: approval or not, optional review comments diff --git a/model/binder.js b/model/binder.js index 41b913bf..cb4ea989 100644 --- a/model/binder.js +++ b/model/binder.js @@ -1,9 +1,12 @@ -var mongoose = require('mongoose'); -var appConfig = require('../config/config').app; -var Schema = mongoose.Schema; -var ObjectId = Schema.Types.ObjectId; +/* eslint-disable no-param-reassign */ +const mongoose = require('mongoose'); +const appConfig = require('../config/config').app; -var share = require('./share.js'); +const {Schema} = mongoose; +const {ObjectId} = Schema.Types; + +const share = require('./share'); +const logger = require('../lib/loggers').getLogger(); /** * finished is the percentage of value that is completed. @@ -11,11 +14,11 @@ var share = require('./share.js'); * If status === 2, then finished = 100, and inProgress = 0; * If status === 0, then finished = 0, and inProgress = 0; * finished and inProgress are cached status. - * totalSteps and finishedteps are cached status. - * They should be updated when the reference traveler is loaed. + * totalSteps and finishedSteps are cached status. + * They should be updated when the reference traveler is loaded. */ -var work = new Schema({ +const work = new Schema({ alias: String, refType: { type: String, @@ -88,7 +91,7 @@ var work = new Schema({ * | 3 // archived */ -var binder = new Schema({ +const binder = new Schema({ title: String, description: String, status: { @@ -167,7 +170,7 @@ function updateInputProgress(w, spec) { } binder.methods.updateWorkProgress = function(spec) { - var w = this.works.id(spec._id); + const w = this.works.id(spec._id); if (!w) { return; } @@ -193,28 +196,28 @@ binder.methods.updateWorkProgress = function(spec) { } else { w.inProgress = spec.finishedInput / spec.totalInput; } + return; + } + // the binder + if (spec.totalValue === 0) { + w.finished = 0; + w.inProgress = 1; } else { - // the binder - if (spec.totalValue === 0) { - w.finished = 0; - w.inProgress = 1; - } else { - w.finished = spec.finishedValue / spec.totalValue; - w.inProgress = spec.inProgressValue / spec.totalValue; - } + w.finished = spec.finishedValue / spec.totalValue; + w.inProgress = spec.inProgressValue / spec.totalValue; } }; binder.methods.updateProgress = function(cb) { - var works = this.works; - var totalValue = 0; - var finishedValue = 0; - var inProgressValue = 0; - var totalWork = 0; - var finishedWork = 0; - var inProgressWork = 0; - var totalInput = 0; - var finishedInput = 0; + const {works} = this; + let totalValue = 0; + let finishedValue = 0; + let inProgressValue = 0; + let totalWork = 0; + let finishedWork = 0; + let inProgressWork = 0; + let totalInput = 0; + let finishedInput = 0; works.forEach(function(w) { totalInput += w.totalInput; finishedInput += w.finishedInput; @@ -243,17 +246,17 @@ binder.methods.updateProgress = function(cb) { this.progressUpdatedOn = Date.now(); this.save(function(err, newBinder) { if (cb) { - return cb(err, newBinder); + cb(err, newBinder); } if (err) { - console.error(err); + logger.error(err); } }); } }; -var Binder = mongoose.model('Binder', binder); +const Binder = mongoose.model('Binder', binder); module.exports = { - Binder: Binder, + Binder, }; diff --git a/model/form.js b/model/form.js index 3fb2996d..46df67c2 100644 --- a/model/form.js +++ b/model/form.js @@ -1,46 +1,55 @@ -var mongoose = require('mongoose'); -var appConfig = require('../config/config').app; -var Schema = mongoose.Schema; -var ObjectId = Schema.Types.ObjectId; -var cheerio = require('cheerio'); -var share = require('./share.js'); -var FormError = require('../lib/error').FormError; -var addHistory = require('./history').addHistory; -var addVersion = require('./history').addVersion; +const mongoose = require('mongoose'); +const cheerio = require('cheerio'); +const appConfig = require('../config/config').app; -/****** +const { Schema } = mongoose; +const { ObjectId } = Schema.Types; +const share = require('./share'); +const { FormError } = require('../lib/error'); +const { addHistory } = require('./history'); +const { addVersion } = require('./history'); +const { addReview } = require('./review'); + +/** **** publicAccess := 0 // for read or | 1 // for write or | -1 // no access -******/ -/****** +***** */ +/** **** status := 0 // editable draft - | 0.5 // ready to release - | 1 // released + | 0.5 // submitted for reviewing + | 1 // ready to release (review passed) | 2 // archived -******/ +***** */ // mapping : user-key -> name // labels : name -> label -var stateTransition = [ +const stateTransition = [ { from: 0, to: [0.5, 2], }, { from: 0.5, - to: [0, 2], + to: [0, 1, 2], + }, + { + from: 1, + to: [2], }, ]; +/** + * 1: the status was used for released before the release form feature + */ const statusMap = { '0': 'draft', - '0.5': 'submitted for release', - '1': 'pre released', + '0.5': 'submitted for review', + '1': 'ready to release', '2': 'archived', }; -var form = new Schema({ +const form = new Schema({ title: String, description: String, createdBy: String, @@ -81,19 +90,19 @@ var form = new Schema({ * and validate unique input name and data-userkey */ form.pre('save', function(next) { - var doc = this; + const doc = this; if (doc.isNew || doc.isModified('html')) { - let mapping = {}; - let labels = {}; - let $ = cheerio.load(doc.html); - let inputs = $('input, textarea'); + const mapping = {}; + const labels = {}; + const $ = cheerio.load(doc.html); + const inputs = $('input, textarea'); let lastInputName = ''; let lastUserkey = ''; let inputName = ''; let label = ''; let userkey = ''; for (let i = 0; i < inputs.length; i += 1) { - let input = $(inputs[i]); + const input = $(inputs[i]); inputName = input.attr('name'); label = input .closest('.control-group') @@ -124,16 +133,14 @@ form.pre('save', function(next) { } else { return next( new FormError( - 'inconsistent usekey "' + - userkey + - '"found for the same input name', + `inconsistent usekey "${userkey}"found for the same input name`, 400 ) ); } } else { return next( - new FormError('duplicated input name "' + inputName + '"', 400) + new FormError(`duplicated input name "${inputName}"`, 400) ); } } else { @@ -142,7 +149,7 @@ form.pre('save', function(next) { if (userkey) { if (mapping.hasOwnProperty(userkey)) { return next( - new FormError('duplicated input userkey "' + userkey + '"', 400) + new FormError(`duplicated input userkey "${userkey}"`, 400) ); } mapping[userkey] = inputName; @@ -154,9 +161,14 @@ form.pre('save', function(next) { doc.mapping = mapping; doc.labels = labels; } - next(); + return next(); }); +form.methods.isBuilder = function() { + const doc = this; + return [0, 0.5, 1].includes(doc.status); +}; + form.plugin(addVersion, { fieldsToVersion: ['title', 'description', 'html'], }); @@ -174,7 +186,9 @@ form.plugin(addHistory, { ], }); -var formFile = new Schema({ +form.plugin(addReview); + +const formFile = new Schema({ form: ObjectId, value: String, inputType: String, @@ -187,11 +201,11 @@ var formFile = new Schema({ uploadedOn: Date, }); -var Form = mongoose.model('Form', form); -var FormFile = mongoose.model('FormFile', formFile); +const Form = mongoose.model('Form', form); +const FormFile = mongoose.model('FormFile', formFile); -var createForm = function(json, newFormResultCallBack) { - var formToCreate = {}; +const createForm = function(json, newFormResultCallBack) { + const formToCreate = {}; formToCreate.title = json.title; formToCreate.createdBy = json.createdBy; formToCreate.createdOn = Date.now(); @@ -204,9 +218,9 @@ var createForm = function(json, newFormResultCallBack) { }; module.exports = { - Form: Form, - FormFile: FormFile, - stateTransition: stateTransition, - statusMap: statusMap, - createForm: createForm, + Form, + FormFile, + stateTransition, + statusMap, + createForm, }; diff --git a/model/history.js b/model/history.js index 10a0dc12..891bfea3 100644 --- a/model/history.js +++ b/model/history.js @@ -122,7 +122,6 @@ function addHistory(schema, options) { * embedded document. Otherwise, explicitly #markModified(path) to mark * modified of the path. * @param {String} userid the user making this update - * @param {Function} cb the callback when save is done */ schema.methods.saveWithHistory = function(userid) { var doc = this; diff --git a/model/released-form.js b/model/released-form.js index 576db1b1..449c6b4a 100644 --- a/model/released-form.js +++ b/model/released-form.js @@ -1,12 +1,13 @@ const mongoose = require('mongoose'); -const Schema = mongoose.Schema; -var addHistory = require('./history').addHistory; -var addVersion = require('./history').addVersion; -/****** +const { Schema } = mongoose; +const { addHistory } = require('./history'); +const { addVersion } = require('./history'); + +/* status := 1 // released | 2 // archived -******/ +*/ const stateTransition = [ { @@ -21,6 +22,7 @@ const statusMap = { }; const formContent = new Schema({ + // _id is the form _id html: String, mapping: Schema.Types.Mixed, labels: Schema.Types.Mixed, diff --git a/model/review.js b/model/review.js new file mode 100644 index 00000000..d8f9ee8d --- /dev/null +++ b/model/review.js @@ -0,0 +1,198 @@ +/* eslint-disable no-param-reassign */ +const mongoose = require('mongoose'); + +const User = mongoose.model('User'); +const { Schema } = mongoose; + +const debug = require('debug')('traveler:review'); +const logger = require('../lib/loggers').getLogger(); + +const reviewRequest = new Schema({ + // use the reviewer _id as review request id, in order to use addToSet + _id: { + type: String, + required: true, + }, + requestedOn: Date, + requestedBy: String, +}); + +// result: 1: approve 2: comment +const reviewResult = new Schema({ + reviewerId: { + type: String, + required: true, + }, + result: { + type: String, + required: true, + }, + v: Number, + submittedOn: Date, + comment: String, +}); + +const review = new Schema({ + policy: { + type: String, + required: true, + default: 'all', + enum: ['all', 'any', 'majority'], + }, + reviewRequests: [reviewRequest], + reviewResults: [reviewResult], +}); + +async function removeReviewRequest(doc, id) { + try { + doc.__review.reviewRequests.id(id).remove(); + await doc.save(); + debug(`${id} removed from ${doc._id}`); + const pull = { reviews: doc._id }; + await User.findByIdAndUpdate(id, { + $pull: pull, + }); + debug(`${doc._id} removed from user ${id}`); + } catch (error) { + logger.error(`request review db error: ${error}`); + throw error; + } +} + +async function closeReviewRequests(doc) { + const requests = doc.__review.reviewRequests; + const pull = { reviews: doc._id }; + let i; + const actions = []; + for (i = 0; i < requests.length; i += 1) { + actions.push( + User.findByIdAndUpdate(requests[i]._id, { + $pull: pull, + }) + ); + } + try { + await Promise.all(actions); + } catch (error) { + logger.error(`request review db error: ${error}`); + throw error; + } +} + +const Review = mongoose.model('Review', review); + +function addReview(schema) { + schema.add({ + __review: review, + }); + + schema.methods.requestReview = async function(requesterId, reviewer) { + const doc = this; + try { + if (!doc.__review) { + doc.__review = { + policy: 'all', + reviewRequests: [], + reviewResults: [], + }; + } + doc.__review.reviewRequests.addToSet({ + _id: reviewer._id, + requestedOn: Date.now(), + requestedBy: requesterId, + }); + const newDoc = await doc.save(); + debug(`doc saved as ${newDoc}`); + reviewer.reviews.addToSet(newDoc._id); + const newReviewer = await reviewer.save(); + debug(`reviewer saved as ${newReviewer}`); + return newDoc; + } catch (error) { + logger.error(`request review db error: ${error}`); + throw error; + } + }; + + schema.methods.removeReviewRequest = async function(id) { + const doc = this; + await removeReviewRequest(doc, id); + }; + + schema.methods.closeReviewRequests = async function() { + const doc = this; + await closeReviewRequests(doc); + }; + + schema.methods.addReviewResult = async function( + reviewerId, + result, + comment, + v + ) { + const doc = this; + try { + doc.__review.reviewResults.push({ + reviewerId, + result, + comment, + submittedOn: Date.now(), + v, + }); + + // if rework (result = 2), then + // 0. set doc status to 0 + // 1. remove doc from reviewer's review list + // 2. remove reviewer from reviewer list + if (result === '2') { + doc.status = 0; + closeReviewRequests(doc); + doc.__review.reviewRequests = []; + } + const newDoc = await doc.save(); + debug(`doc saved as ${newDoc}`); + return newDoc; + } catch (error) { + logger.error(`update review db error: ${error}`); + throw error; + } + }; + + schema.methods.allApproved = function() { + const doc = this; + if (!doc.__review) { + return false; + } + const { reviewRequests = [], reviewResults = [] } = doc.__review; + if (reviewRequests.length === 0) { + return false; + } + const approval = {}; + let i; + debug(`has ${reviewResults.length} results`); + debug(`has ${reviewRequests.length} requests`); + for (i = reviewResults.length - 1; i >= 0; i -= 1) { + debug(reviewResults[i].result); + if ( + reviewResults[i].result !== '1' && + !approval[reviewResults[i].reviewerId] + ) { + return false; + } + if (reviewResults[i].result === '1') { + approval[reviewResults[i].reviewerId] = true; + } + } + for (i = 0; i < reviewRequests.length; i += 1) { + if (!approval[reviewRequests[i]._id]) { + return false; + } + } + return true; + }; +} + +module.exports = { + review, + reviewResult, + addReview, +}; diff --git a/model/user.js b/model/user.js index a0957b04..a5e61492 100644 --- a/model/user.js +++ b/model/user.js @@ -1,8 +1,9 @@ -var mongoose = require('mongoose'); -var Schema = mongoose.Schema; -var ObjectId = Schema.Types.ObjectId; +const mongoose = require('mongoose'); -var user = new Schema({ +const { Schema } = mongoose; +const { ObjectId } = Schema.Types; + +const user = new Schema({ _id: String, name: String, email: String, @@ -14,28 +15,30 @@ var user = new Schema({ forms: [ObjectId], travelers: [ObjectId], binders: [ObjectId], + // form reviews + reviews: [ObjectId], subscribe: { type: Boolean, default: false, }, }); -var group = new Schema({ +const group = new Schema({ _id: String, name: String, deleted: { type: Boolean, - default: false + default: false, }, - members: [{type: String, ref: 'User'}], + members: [{ type: String, ref: 'User' }], forms: [ObjectId], travelers: [ObjectId], binders: [ObjectId], }); -var User = mongoose.model('User', user); -var Group = mongoose.model('Group', group); +const User = mongoose.model('User', user); +const Group = mongoose.model('Group', group); module.exports = { - User: User, - Group: Group, + User, + Group, }; diff --git a/package-lock.json b/package-lock.json index ec7f880e..109fa3b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "traveler-aps", - "version": "1.5.0", + "version": "1.5.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -30,40 +30,39 @@ "js-tokens": "^4.0.0" } }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "@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": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" + "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" } }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "@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": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" } }, - "@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true }, "@sinonjs/commons": { "version": "1.3.0", @@ -94,33 +93,33 @@ "lodash.get": "^4.4.2" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dev": true, + "@types/bson": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz", + "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==", "requires": { - "@types/minimatch": "*", "@types/node": "*" } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, "@types/node": { - "version": "14.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.1.tgz", - "integrity": "sha512-aYNbO+FZ/3KGeQCEkNhHFRIzBOUgc7QvcVNKXbfnhDkSfwUv91JsQQa10rDgKSTSLkXZ1UIyPe4FJJNVgw1xWQ==", - "dev": true + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -128,6 +127,12 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "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 + }, "CSSselect": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/CSSselect/-/CSSselect-0.4.1.tgz", @@ -152,9 +157,9 @@ } }, "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, "acorn-globals": { @@ -173,9 +178,9 @@ } }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true }, "aggregate-error": { @@ -189,9 +194,9 @@ } }, "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "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", @@ -215,27 +220,33 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, + "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": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "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, "requires": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" }, "dependencies": { "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "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 } } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "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": { @@ -246,12 +257,6 @@ "color-convert": "^1.9.0" } }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -272,17 +277,35 @@ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, - "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 + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } }, "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, "asap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", @@ -305,26 +328,11 @@ "dev": true }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "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.1.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", - "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", - "requires": { - "lodash": "^4.14.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - } - } - }, "aws-sign2": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", @@ -345,9 +353,9 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "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==" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "basic-auth": { "version": "1.0.0", @@ -355,49 +363,47 @@ "integrity": "sha1-ERstn/jk5tE2uMhOpeCWy4c1Fjc=" }, "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "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": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" }, "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "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==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "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" } }, - "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==" - }, "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "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.2.0" + "safe-buffer": "~5.1.0" } } } }, "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "body-parser": { "version": "1.19.0", @@ -465,23 +471,23 @@ "dev": true }, "bson": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", - "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" }, "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, "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==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "bunyan": { "version": "1.5.1", @@ -507,13 +513,14 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "callback-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz", - "integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=", + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "> 1.0.0 < 3.0.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" } }, "caller-callsite": { @@ -613,6 +620,16 @@ "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" + } + } } }, "character-parser": { @@ -620,12 +637,6 @@ "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz", "integrity": "sha1-wN3kqxgnE7kZuXCVmhI+zBow/NY=" }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -704,9 +715,9 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "lodash.merge": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", - "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "readable-stream": { "version": "2.3.6", @@ -773,64 +784,52 @@ } }, "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "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=", + "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": { - "number-is-nan": "^1.0.0" + "color-convert": "^2.0.1" } }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "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": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "color-name": "~1.1.4" } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "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 + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" } } } }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -848,12 +847,6 @@ } } }, - "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 - }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -877,14 +870,20 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, + "colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true + }, "colornames": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", @@ -914,9 +913,9 @@ } }, "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==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.2.0.tgz", + "integrity": "sha512-LLKxDvHeL91/8MIyTAD5BFMNtoIwztGPMiM/7Bl8rIPmHCZXRxmSWr91h57dpOpnQ6jIUqEWdXE/uBYMfiVZDA==", "dev": true }, "commist": { @@ -978,45 +977,47 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "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" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, + "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==" + }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } } } }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, "constantinople": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.2.tgz", @@ -1190,15 +1191,6 @@ "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", "optional": 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" - } - }, "dashdash": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.10.1.tgz", @@ -1207,24 +1199,18 @@ "assert-plus": "0.1.x" } }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" }, "dependencies": { "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.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -1233,12 +1219,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -1249,50 +1229,18 @@ } }, "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=", + "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==", "dev": true }, - "del": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", - "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "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==", "dev": true, "requires": { - "globby": "^10.0.1", - "graceful-fs": "^4.2.2", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.1", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0" - }, - "dependencies": { - "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" - } - }, - "rimraf": { - "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" - } - } + "object-keys": "^1.0.12" } }, "delayed-stream": { @@ -1301,6 +1249,11 @@ "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", "optional": true }, + "denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1336,15 +1289,6 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "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" - } - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1401,41 +1345,37 @@ } }, "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", "stream-shift": "^1.0.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "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" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, + "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==" + }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } } } @@ -1445,12 +1385,6 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1488,6 +1422,15 @@ } } }, + "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": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -1516,69 +1459,43 @@ "escape-html": "~1.0.3" } }, - "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" - } - }, - "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" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - }, - "dependencies": { - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - } - } - }, - "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==", + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "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==", + "dev": true, "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "escape-html": { @@ -1602,88 +1519,189 @@ } }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "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.0.0", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "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", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", + "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": { + "@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, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "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 + }, + "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" + } + }, + "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-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "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" } }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "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": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "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 }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "lru-cache": "^6.0.0" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "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 + }, + "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" + } + }, + "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" + } } } }, + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, "eslint-config-prettier": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", @@ -1693,6 +1711,231 @@ "get-stdin": "^6.0.0" } }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "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 + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.0.tgz", + "integrity": "sha512-hqSE88MmHl3ru9SYvDyGrlo0JwROlf9fiEMplEV7j/EAuq9iSlIlyCFbBT6pdULQBSnBYtYKiMLps+hKkyP7Gg==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "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" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "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 + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.6.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.4", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.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" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1712,35 +1955,49 @@ } }, "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "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": "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==", + "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": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "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.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" }, "dependencies": { "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "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 } } @@ -1751,9 +2008,9 @@ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "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" @@ -1800,15 +2057,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -1917,37 +2165,6 @@ "resolved": "https://registry.npmjs.org/express-slash/-/express-slash-1.0.0.tgz", "integrity": "sha1-4ebOjJEWEt2rU3LBC+AuqIQpgus=" }, - "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" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extsprintf": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", @@ -1959,31 +2176,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "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" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.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", @@ -2001,44 +2193,26 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" }, - "fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fecha": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "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": "^2.0.1" + "flat-cache": "^3.0.4" } }, "file-stream-rotator": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.0.5.tgz", - "integrity": "sha1-ypn2tClZsLf4LB5LfHwtK43ovjo=", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz", + "integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==", "requires": { - "moment": "2.3.1" + "moment": "^2.11.2" } }, "fill-range": { @@ -2085,20 +2259,19 @@ } }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" }, "dependencies": { "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2110,9 +2283,9 @@ } }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "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" @@ -2121,9 +2294,9 @@ } }, "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "forever-agent": { @@ -2171,6 +2344,12 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "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", @@ -2183,6 +2362,17 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -2204,6 +2394,16 @@ "pump": "^3.0.0" } }, + "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==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", @@ -2217,129 +2417,13 @@ "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=", - "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=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "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" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "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" - } - } - } - }, "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "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==", + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "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" - }, - "dependencies": { - "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" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } + "type-fest": "^0.20.2" } }, "graceful-fs": { @@ -2359,28 +2443,41 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "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": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } + "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "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==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "hawk": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", @@ -2405,14 +2502,50 @@ "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" }, "help-me": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz", - "integrity": "sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", "requires": { - "callback-stream": "^1.0.2", - "glob-stream": "^6.1.0", - "through2": "^2.0.1", - "xtend": "^4.0.0" + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + } + }, + "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==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "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==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } } }, "hoek": { @@ -2422,9 +2555,9 @@ "optional": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "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 }, "htmlparser2": { @@ -2486,6 +2619,12 @@ } } }, + "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 + }, "husky": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/husky/-/husky-3.1.0.tgz", @@ -2522,9 +2661,9 @@ } }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { "version": "4.0.6", @@ -2533,9 +2672,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "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", @@ -2576,92 +2715,15 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "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 - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.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" - } - } + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" } }, "ipaddr.js": { @@ -2669,26 +2731,60 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "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==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", @@ -2698,7 +2794,8 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -2715,42 +2812,31 @@ "is-extglob": "^2.1.1" } }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "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-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", "dev": true, "requires": { - "symbol-observable": "^1.1.0" + "has-tostringtag": "^1.0.0" } }, - "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==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, "is-promise": { @@ -2758,37 +2844,59 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "requires": { - "is-unc-path": "^1.0.0" - } + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "has-tostringtag": "^1.0.0" } }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } }, "isarray": { "version": "0.0.1", @@ -2878,13 +2986,31 @@ "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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "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.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "jstransformer": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", @@ -2909,9 +3035,9 @@ "dev": true }, "kareem": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.7.tgz", - "integrity": "sha512-p8+lEpsNs4N0fvNOC1/zzDO0wDrD3Pb1G+OwfIG+gKVK3MyY5jeaGYh+9Qx6jb4fEG2b3E6U98vaE9MH7Gilsw==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", + "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" }, "kind-of": { "version": "3.2.2", @@ -2965,13 +3091,13 @@ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "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, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "lines-and-columns": { @@ -2981,27 +3107,40 @@ "dev": true }, "lint-staged": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-9.5.0.tgz", - "integrity": "sha512-nawMob9cb/G1J98nb8v3VC/E8rcX1rryUYXVZ69aT9kde6YWX+uvNOEHY5yf2gcWcTJGiD0kqXmCnS3oD75GIA==", + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-11.2.3.tgz", + "integrity": "sha512-Tfmhk8O2XFMD25EswHPv+OYhUjsijy5D7liTdxeXvhG2rsadmOLFtyj8lmlfoFFXY8oXWAIOKpoI+lJe1DB1mw==", "dev": true, "requires": { - "chalk": "^2.4.2", - "commander": "^2.20.0", - "cosmiconfig": "^5.2.1", - "debug": "^4.1.1", - "dedent": "^0.7.0", - "del": "^5.0.0", - "execa": "^2.0.3", - "listr": "^0.14.3", - "log-symbols": "^3.0.0", - "micromatch": "^4.0.2", + "cli-truncate": "2.1.0", + "colorette": "^1.4.0", + "commander": "^8.2.0", + "cosmiconfig": "^7.0.1", + "debug": "^4.3.2", + "enquirer": "^2.3.6", + "execa": "^5.1.1", + "listr2": "^3.12.2", + "micromatch": "^4.0.4", "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.1.1", - "string-argv": "^0.3.0", - "stringify-object": "^3.3.0" + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "3.3.0", + "supports-color": "8.1.1" }, "dependencies": { + "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" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3013,52 +3152,76 @@ "which": "^2.0.1" } }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "execa": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", - "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", + "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.0", - "get-stream": "^5.0.0", + "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": "^3.0.0", - "onetime": "^5.1.0", - "p-finally": "^2.0.0", - "signal-exit": "^3.0.2", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } + "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 + }, + "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 }, "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "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 }, "npm-run-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", - "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "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" } }, - "p-finally": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", - "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", - "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-key": { "version": "3.1.1", @@ -3081,6 +3244,15 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "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", @@ -3092,175 +3264,31 @@ } } }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "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 - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "listr2": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.12.2.tgz", + "integrity": "sha512-64xC2CJ/As/xgVI3wbhlPWVPx0wfTqbUAkpb7bjDi0thSWMqrf07UFhrfsGoo8YSXmF049Rp9C0cjLC8rZxK9A==", "dev": true, "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "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" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "cli-truncate": "^2.1.0", + "colorette": "^1.4.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.7", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" } }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "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 - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": 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": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" } }, "locate-path": { @@ -3273,9 +3301,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.assignin": { "version": "4.2.0", @@ -3320,7 +3348,8 @@ "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true }, "lodash.isplainobject": { "version": "4.0.6", @@ -3337,10 +3366,16 @@ "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" }, + "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.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==" + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" }, "lodash.pick": { "version": "4.4.0", @@ -3362,64 +3397,57 @@ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true }, "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" }, "dependencies": { - "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 - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "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": { - "restore-cursor": "^2.0.0" + "color-convert": "^2.0.1" } }, - "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 - }, - "onetime": { + "color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "color-name": "~1.1.4" } }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "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 + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } @@ -3459,6 +3487,15 @@ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, + "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" + } + }, "marked": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", @@ -3469,6 +3506,12 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -3480,12 +3523,6 @@ "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", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, "method-override": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", @@ -3518,13 +3555,13 @@ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "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.0.5" + "picomatch": "^2.2.3" } }, "mime": { @@ -3638,43 +3675,64 @@ } }, "moment": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.3.1.tgz", - "integrity": "sha1-2nPXD2Jla7W36vA4f3k79hITEuM=" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "mongodb": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.8.tgz", - "integrity": "sha512-mj7yIUyAr9xnO2ev8pcVJ9uX7gSum5LLs1qIFoWLxA5Il50+jcojKtaO1/TbexsScZ9Poz00Pc3b86GiSqJ7WA==", - "requires": { - "mongodb-core": "3.0.8" - } - }, - "mongodb-core": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.8.tgz", - "integrity": "sha512-dFxfhH9N7ohuQnINyIl6dqEF8sYOE0WKuymrFf3L3cipJNrx+S8rAbNOTwa00/fuJCjBMJNFsaA+R2N16//UIw==", - "requires": { - "bson": "~1.0.4", - "require_optional": "^1.0.1" + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", + "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + }, + "dependencies": { + "optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "requires": { + "require-at": "^1.0.6" + } + } } }, "mongoose": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.18.tgz", - "integrity": "sha512-13+gKxvViIVp4esPkgQYTdfisLLWxghvsj7aBg4oBurvb0LAXcSXFZpFORZnxtOdyxM7kJiSV9kjFjerEm3Bhw==", - "requires": { - "async": "2.1.4", - "bson": "~1.0.5", - "kareem": "2.0.7", - "lodash.get": "4.4.2", - "mongodb": "3.0.8", + "version": "5.13.13", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.13.tgz", + "integrity": "sha512-M55tpCr/p5i6vdJ54nm4MG6/7SKV4JqlWnqbx6yCRuAuW05CZ7u+gNuHVPQVF9dZ59ALXjOtPEUl+OXklAa7ng==", + "requires": { + "@types/bson": "1.x || 4.0.x", + "@types/mongodb": "^3.5.27", + "bson": "^1.1.4", + "kareem": "2.3.2", + "mongodb": "3.7.3", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.4.1", - "mquery": "3.0.0", - "ms": "2.0.0", - "regexp-clone": "0.0.1", + "mpath": "0.8.4", + "mquery": "3.2.5", + "ms": "2.1.2", + "optional-require": "1.0.x", + "regexp-clone": "1.0.0", + "safe-buffer": "5.2.1", + "sift": "13.5.2", "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "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==" + } } }, "mongoose-legacy-pluralize": { @@ -3721,107 +3779,140 @@ } }, "mpath": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.4.1.tgz", - "integrity": "sha512-NNY/MpBkALb9jJmjpBlIi6GRoLveLUM0pJzgbp9vY9F7IQEb/HREC/nxrixechcQwd1NevOhJnWWV8QQQRE+OA==" + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==" }, "mqtt": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.1.tgz", - "integrity": "sha512-Iv893r+jWlo5GkNcPOfCGwW8M49IixwHiKLFFYTociEymSibUVCORVEjPXWPGzSxhn7BdlUeHicbRmWiv0Crkg==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", + "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", "requires": { - "base64-js": "^1.3.0", "commist": "^1.0.0", - "concat-stream": "^1.6.2", + "concat-stream": "^2.0.0", "debug": "^4.1.1", - "end-of-stream": "^1.4.1", - "es6-map": "^0.1.5", - "help-me": "^1.0.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", "inherits": "^2.0.3", "minimist": "^1.2.5", - "mqtt-packet": "^6.3.2", + "mqtt-packet": "^6.8.0", "pump": "^3.0.0", - "readable-stream": "^2.3.6", + "readable-stream": "^3.6.0", "reinterval": "^1.1.0", - "split2": "^3.1.0", - "ws": "^7.3.1", - "xtend": "^4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, + "split2": "^3.1.0", + "ws": "^7.5.0", + "xtend": "^4.0.2" + }, + "dependencies": { "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "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" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, + "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==" + }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } }, "mqtt-packet": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.6.0.tgz", - "integrity": "sha512-LvghnKMFC70hKWMVykmhJarlO5e7lT3t9s9A2qPCUx+lazL3Mq55U+eCV0eLi7/nRRQYvEUWo/2tTo89EjnCJQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", "requires": { "bl": "^4.0.2", "debug": "^4.1.1", "process-nextick-args": "^2.0.1" }, "dependencies": { + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "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==" + }, + "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==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "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==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } } } }, "mquery": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.0.0.tgz", - "integrity": "sha512-WL1Lk8v4l8VFSSwN3yCzY9TXw+fKVYKn6f+w86TRzOLSE8k1yTgGaLBPUByJQi8VcLbOdnUneFV/y3Kv874pnQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", + "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", "requires": { - "bluebird": "3.5.0", - "debug": "2.6.9", - "regexp-clone": "0.0.1", - "sliced": "0.0.5" + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "^1.0.0", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" } - }, - "sliced": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", - "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" } } }, @@ -3846,12 +3937,6 @@ } } }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", @@ -3864,12 +3949,20 @@ }, "dependencies": { "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "optional": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "optional": true + } } } } @@ -3897,11 +3990,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -3980,12 +4068,52 @@ "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", "optional": 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-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -4041,62 +4169,19 @@ "integrity": "sha512-HZubVd6IfHsbnpdNF/ICaSAzBUEW1TievpkjY3tB4Jnk8L7+pJ3conPzUt3Mn/6OZx9uzTDOHYPGA8/AxYHBOg==" }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "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" - } - }, - "ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "requires": { - "readable-stream": "^2.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "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" - } - } + "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" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -4122,9 +4207,9 @@ } }, "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "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" @@ -4160,11 +4245,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4183,9 +4263,9 @@ "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==", "dev": true }, "path-to-regexp": { @@ -4214,10 +4294,21 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "pkg-dir": { @@ -4229,6 +4320,66 @@ "find-up": "^4.0.0" } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -4239,27 +4390,18 @@ } }, "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "dependencies": { "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -4269,9 +4411,9 @@ "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "prettier": { @@ -4323,27 +4465,6 @@ "once": "^1.3.1" } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "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==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -4394,17 +4515,98 @@ "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "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" + "@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" + } + }, + "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 + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "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" } }, - "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 + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } } } }, @@ -4428,14 +4630,14 @@ } }, "regexp-clone": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", - "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", + "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "reinterval": { @@ -4443,11 +4645,6 @@ "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" }, - "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=" - }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -4484,14 +4681,16 @@ } } }, - "require_optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", - "requires": { - "resolve-from": "^2.0.0", - "semver": "^5.1.0" - } + "require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" + }, + "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 }, "resolve": { "version": "1.17.0", @@ -4502,11 +4701,6 @@ "path-parse": "^1.0.6" } }, - "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -4517,12 +4711,6 @@ "signal-exit": "^3.0.2" } }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -4540,28 +4728,16 @@ "glob": "^6.0.1" } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, "run-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", "dev": true }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -4647,10 +4823,20 @@ } } }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true }, "semver-compare": { "version": "1.0.0", @@ -4760,6 +4946,22 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "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==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "sift": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", + "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -4820,20 +5022,38 @@ "dev": true }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "dependencies": { - "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=", + "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 } } @@ -4860,6 +5080,15 @@ "amdefine": ">=0.0.4" } }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -4967,25 +5196,34 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "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": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -5005,22 +5243,20 @@ } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": "^4.1.0" - }, - "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 - } + "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -5040,59 +5276,53 @@ "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==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.0.2.tgz", + "integrity": "sha512-ii6tc8ImGFrgMPYq7RVAMKkhPo9vk8uA+D3oKbJq/3Pk2YSMv1+9dUAesa9UxMbxBTvxwKTQffBahNVNxEvM8Q==", + "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "dev": true + } } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", + "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { - "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 - }, - "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=", - "dev": true - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "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==", + "ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "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 } } }, @@ -5205,75 +5435,10 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "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" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "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" - } - }, - "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" - } - } - } - }, - "through2-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", - "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - } - }, "tinymce": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.0.3.tgz", - "integrity": "sha512-x7D0ttqn176Z4CWQIITMjcEtMGUjiaK0Y2FN2vO8DiyUeIHCdr70jJDSoGhuXtXX70Sw4Bd5UFfqIn/bG3HXcg==" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", - "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" - } + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.10.1.tgz", + "integrity": "sha512-aIsFTYiuESpoYkCgkoojpVtPwrSvYBxp4mMEGsj20CnUruLCWosywkbYHDII+j7KlQZZn3p+xK89f5gT3QyuGw==" }, "to-regex-range": { "version": "5.0.1", @@ -5346,10 +5511,30 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tsconfig-paths": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tunnel-agent": { @@ -5358,18 +5543,13 @@ "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", "optional": true }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -5379,9 +5559,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "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 }, "type-is": { @@ -5428,18 +5608,16 @@ "random-bytes": "~1.0.0" } }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" - }, - "unique-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, "requires": { - "json-stable-stringify-without-jsonify": "^1.0.1", - "through2-filter": "^3.0.0" + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" } }, "unpipe": { @@ -5448,9 +5626,9 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "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" @@ -5475,9 +5653,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "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": { @@ -5530,6 +5708,19 @@ "isexe": "^2.0.0" } }, + "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==", + "dev": true, + "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" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -5560,9 +5751,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "readable-stream": { "version": "3.2.0", @@ -5650,45 +5841,39 @@ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" }, "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "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": "^2.1.1", - "strip-ansi": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.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 - }, - "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=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "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": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "color-convert": "^2.0.1" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "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": { - "ansi-regex": "^3.0.0" + "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 } } }, @@ -5697,42 +5882,28 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==" }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index 2ec8970f..99a8c318 100644 --- a/package.json +++ b/package.json @@ -16,21 +16,21 @@ "cheerio": "0.22.0", "compression": "1.7.3", "cookie-parser": "1.4.4", - "debug": "^4.1.1", + "debug": "^4.3.2", "errorhandler": "1.5.0", "express": "^4.17.1", "express-session": "1.15.6", "express-slash": "1.0.0", - "file-stream-rotator": "0.0.5", + "file-stream-rotator": "^0.5.7", "helmet": "^4.6.0", "jade": "1.10.0", "jstransformer-marked": "1.0.1", "ldapjs": "1.0.0", - "lodash": "^4.17.11", + "lodash": "^4.17.21", "method-override": "3.0.0", - "mongoose": "^5.0.18", + "mongoose": "^5.13.13", "morgan": "1.9.1", - "mqtt": "4.2.1", + "mqtt": "^4.2.8", "multer": "0.1.0", "optional-require": "^1.0.2", "pause": "0.0.1", @@ -38,19 +38,22 @@ "sanitize-html": "^1.20.0", "serve-favicon": "2.5.0", "templatizer": "0.2.10", - "tinymce": "5.0.3", + "tinymce": "^5.10.1", "winston": "^3.2.1" }, "devDependencies": { - "acorn": "^7.1.0", + "acorn": "^7.4.1", "chai": "^4.2.0", - "eslint": "^6.8.0", + "eslint": "^7.32.0", + "eslint-config-airbnb-base": "^14.2.1", "eslint-config-prettier": "^6.9.0", + "eslint-plugin-import": "^2.24.2", "husky": "^3.1.0", - "lint-staged": "^9.5.0", + "lint-staged": "^11.2.3", "mocha": "^5.2.0", "prettier": "^1.19.1", - "sinon": "^7.2.3" + "sinon": "^7.2.3", + "supports-color": "^9.0.2" }, "husky": { "hooks": { @@ -58,9 +61,9 @@ } }, "lint-staged": { - "*.{js,json,css,md}": [ - "prettier --config ./prettier.config.js --write", - "git add" + "*.js": [ + "prettier --write", + "eslint" ] } } diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index 5a673461..e689acb3 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1,12 +1,13 @@ -/* global formStatus: false, window: false, FormData: false*/ +/* eslint-disable no-param-reassign */ +/* global formStatus, released_form_version_mgmt, selectColumn, titleColumn, versionColumn, releasedOnColumn, releasedByColumn, releasedFormLinkColumn, sDomPage, selectMultiEvent, filterEvent, fnAddFilterFoot, selectOneEvent, fnGetSelectedInPage, id */ /* global tinymce: false, rivets: false, UID: false, input: false, spec: false, - ajax401: false, disableAjaxCache:false, prefix: false, updateAjaxURL: false, - formType, livespan*/ + ajax401: false, disableAjaxCache:false, prefix: false, updateAjaxURL: false, + formType, livespan, Holder, moment */ /* eslint max-nested-callbacks: [2, 4], complexity: [2, 20] */ -var mce_content = { +const mce_content = { selector: 'textarea.tinymce', content_css: '/bootstrap/css/bootstrap.css', browser_spellcheck: true, @@ -22,7 +23,7 @@ var mce_content = { statusbar: false, }; -var initHtml = ''; +let initHtml = ''; /** * send request with data, and exec cb on response @@ -34,39 +35,44 @@ var initHtml = ''; * @return {void} */ function sendRequest(data, cb, option) { - var path = window.location.pathname; - var url; - var type; + const path = window.location.pathname; + let url; + let type; if (option === 'saveas') { - url = prefix + '/forms/'; + url = `${prefix}/forms/`; + type = 'POST'; + } else if (option === 'review') { + url = `${path}review/results`; type = 'POST'; } else if (option === 'status') { - url = path + 'status'; + url = `${path}status`; type = 'PUT'; } else if (option === 'release') { - url = path + 'released'; + url = `${path}released`; type = 'PUT'; } else { url = path; type = 'PUT'; } $.ajax({ - url: url, - type: type, + url, + type, async: true, data: JSON.stringify(data), contentType: 'application/json', processData: false, }) - .done(function(data, textStatus, request) { - var timestamp = request.getResponseHeader('Date'); - if (data.location) { - document.location.href = data.location; + .done(function(responseData, textStatus, request) { + const timestamp = request.getResponseHeader('Date'); + if (responseData.location) { + $('#message').append( + `
New resource is available at ${responseData.location}
` + ); } else { $('#message').append( - '
The changes were saved ' + - livespan(timestamp) + - '.
' + `
The changes were saved ${livespan( + timestamp + )}.
` ); } $.livestamp.resume(); @@ -76,54 +82,49 @@ function sendRequest(data, cb, option) { }) .fail(function() { $('form#output').fadeTo('slow', 1); - }) - .always(function() {}); + }); } -function archive_prior_released_forms(selected) { - if (!selected || selected.length < 1) { +function archive_prior_released_forms(target) { + if (!target) { return; } - - $.ajax({ - url: '/released-forms/archive', - type: 'PUT', - async: true, - data: JSON.stringify(selected), - contentType: 'application/json', - processData: false, - }) - .done(function(data, textStatus, request) { - $('#message').append( - '
' + - data + - '
' - ); - }) - .fail(function(data, textStatus, request) { - $('#message').append( - '
' + - data + - '
' - ); + for (const [key, value] of Object.entries(target)) { + $.ajax({ + url: `/released-forms/${key}/status`, + type: 'PUT', + async: false, + data: JSON.stringify(value), + contentType: 'application/json', + processData: false, }) - .always(function() {}); + .done(function(data) { + $('#message').append( + `
${data}
` + ); + }) + .fail(function(data) { + $('#message').append( + `
${data}
` + ); + }); + } } function userkey_error($userkey, msg) { if (!$userkey.closest('.control-group').hasClass('error')) { $userkey .closest('.controls') - .append('
' + msg + '
') + .append(`
${msg}
`) .closest('.control-group') .addClass('error'); } } function updateSectionNumbers() { - var sectionNumber = 0; - var instructionNumber = 0; - var controlNumber = 0; + let sectionNumber = 0; + let instructionNumber = 0; + let controlNumber = 0; // assign the sequence number to all legend $('#output') .find('legend, .control-label, .rich-instruction') @@ -138,19 +139,17 @@ function updateSectionNumbers() { .text(sectionNumber); } else if ($(this).is('div.rich-instruction')) { instructionNumber += 1; - //reset control number + // reset control number controlNumber = 0; $(this) .find('.rich-instruction-number') - .text('' + sectionNumber + '.' + instructionNumber); + .text(`${sectionNumber}.${instructionNumber}`); } else { controlNumber += 1; $(this) .find('.control-number') - .text( - '' + sectionNumber + '.' + instructionNumber + '.' + controlNumber - ); + .text(`${sectionNumber}.${instructionNumber}.${controlNumber}`); } }); } @@ -162,7 +161,7 @@ function addSectionNumbers() { if ($(this).is('legend')) { prependSpanIfNotExists(this, 'section-number'); } else if ($(this).is('div.tinymce')) { - var instructionParent = this.parentElement; + const instructionParent = this.parentElement; addSectionNumberToRichInstruction(instructionParent); } else { prependSpanIfNotExists(this, 'control-number'); @@ -171,23 +170,24 @@ function addSectionNumbers() { } function addSectionNumberToRichInstruction(richInstructionParent) { - if (richInstructionParent.className != 'rich-instruction') { - var tinymceChild = $(richInstructionParent).find('.tinymce')[0]; + let target = richInstructionParent; + if (richInstructionParent.className !== 'rich-instruction') { + const tinymceChild = $(richInstructionParent).find('.tinymce')[0]; richInstructionParent.removeChild(tinymceChild); - var richInstructionDiv = document.createElement('div'); + const richInstructionDiv = document.createElement('div'); richInstructionDiv.className = 'rich-instruction'; richInstructionDiv.appendChild(tinymceChild); richInstructionParent.appendChild(richInstructionDiv); - richInstructionParent = richInstructionDiv; + target = richInstructionDiv; } - prependSpanIfNotExists(richInstructionParent, 'rich-instruction-number'); + prependSpanIfNotExists(target, 'rich-instruction-number'); } function prependSpanIfNotExists(element, sectionName) { - if ($(element).find('.' + sectionName).length === 0) { - $(element).prepend(' '); + if ($(element).find(`.${sectionName}`).length === 0) { + $(element).prepend(` `); } } @@ -195,8 +195,8 @@ function done_button(view, $out) { return function(e) { e.preventDefault(); // validate the userkey according to current form - var userKeyInput = $('.well.spec input[name="userkey"]'); - var userkey = userKeyInput.val(); + const userKeyInput = $('.well.spec input[name="userkey"]'); + let userkey = userKeyInput.val(); if (typeof userkey !== 'undefined') { userkey = userkey.trim(); if (!userKeyInput[0].validity.valid) { @@ -206,9 +206,7 @@ function done_button(view, $out) { if ( userkey && $( - '.control-group-wrap[data-status!="editing"] input[data-userkey="' + - userkey + - '"]' + `.control-group-wrap[data-status!="editing"] input[data-userkey="${userkey}"]` ).length >= 1 ) { userkey_error(userKeyInput, 'duplicated userkey found'); @@ -281,7 +279,7 @@ function binding($edit, $out, model, $done) { }); $('input[type="number"]', $edit).on('input', function() { - var val = $(this) + const val = $(this) .val() .trim(); if (val === '') { @@ -302,8 +300,8 @@ function binding($edit, $out, model, $done) { model[$(this).attr('name')] = $(this).prop('checked'); }); - var view = rivets.bind($out, { - model: model, + const view = rivets.bind($out, { + model, }); // clean all click handlers to the $done button first @@ -325,21 +323,21 @@ function binding($edit, $out, model, $done) { */ function add_radio($radio_group, $radio_value_spec, $done, count, model) { // Add radio button text configuration screen - var $radio_text = $( - spec.generic_text_input({ label: 'Radio button ' + count + ' value' }) + const $radio_text = $( + spec.generic_text_input({ label: `Radio button ${count} value` }) ); - $('input', $radio_text).attr('name', 'radio_text_' + count); + $('input', $radio_text).attr('name', `radio_text_${count}`); $($radio_value_spec).append($radio_text); // Add radio button input control - var $radio_button_control = $(input.radio_button()); + const $radio_button_control = $(input.radio_button()); $('input', $radio_button_control).attr( 'rv-value', - 'model.radio_text_' + count + `model.radio_text_${count}` ); $('span.radio_text', $radio_button_control).attr( 'rv-text', - 'model.radio_text_' + count + `model.radio_text_${count}` ); $radio_group.find('.controls').append($radio_button_control); @@ -352,24 +350,24 @@ function add_radio($radio_group, $radio_value_spec, $done, count, model) { $radio_text.on('click', 'button[value="remove-radio-button"]', function(e) { e.preventDefault(); - var value = $(e.delegateTarget) + const value = $(e.delegateTarget) .find('input') .val(); - var name = $(e.delegateTarget) + const name = $(e.delegateTarget) .find('input') .prop('name'); - var radio = $radio_group - .find('input[type="radio"][value="' + value + '"]') + const radio = $radio_group + .find(`input[type="radio"][value="${value}"]`) .parent(); model[name] = undefined; radio.remove(); e.delegateTarget.remove(); }); - var radio_text = 'radio_text_' + count; + let radio_text = `radio_text_${count}`; - if (model['radio_text_' + count]) { - radio_text = model['radio_text_' + count]; + if (model[`radio_text_${count}`]) { + radio_text = model[`radio_text_${count}`]; } $('input', $radio_text).val(radio_text); @@ -378,29 +376,29 @@ function add_radio($radio_group, $radio_value_spec, $done, count, model) { function radio_edit($cgr) { $('#output .well.spec').remove(); - var radio_group_name; + let radio_group_name; - var label = 'label'; - var userkey = ''; - var required = false; + let label = 'label'; + let userkey = ''; + let required = false; // get all input components - var $radio_group = $(input.radiogroup()); - var $buttons = $(input.button()); + const $radio_group = $(input.radiogroup()); + const $buttons = $(input.button()); // get configuration (spec) view components - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $required = $(spec.required()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $required = $(spec.required()); - var $add_radio_button = $(spec.add_radio_button()); - var $radio_value_spec = $('
'); - var $done = $(spec.done()); + const $add_radio_button = $(spec.add_radio_button()); + const $radio_value_spec = $('
'); + const $done = $(spec.done()); - var radio_button_count = 0; + let radio_button_count = 0; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); - var inputs = $cgr.find('.controls').find('input'); + const inputs = $cgr.find('.controls').find('input'); if (inputs.length > 0) { radio_group_name = inputs[0].name; userkey = $(inputs[0]).data('userkey'); @@ -413,7 +411,7 @@ function radio_edit($cgr) { } // Assign components to the configure view - var $edit = $('
').append( + const $edit = $('
').append( $label, $userkey, $required, @@ -422,14 +420,14 @@ function radio_edit($cgr) { $done ); - var $new_cgr = $( + const $new_cgr = $( '
radio
' ).append($radio_group); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - required: required, + const model = { + label, + userkey, + required, name: radio_group_name, }; $('input', $label).val(label); @@ -439,12 +437,12 @@ function radio_edit($cgr) { // add all the radio buttons if ($cgr) { // load the radio buttons for edit mode - var radio_buttons = $cgr.find('.controls').find('input'); + const radio_buttons = $cgr.find('.controls').find('input'); $.map(radio_buttons, function(button, i) { - model['radio_text_' + i] = $(button).prop('value'); + model[`radio_text_${i}`] = $(button).prop('value'); }); - var length = radio_buttons.size(); - for (var i = 0; i < length; i++) { + const length = radio_buttons.size(); + for (let i = 0; i < length; i += 1) { add_radio( $radio_group, $radio_value_spec, @@ -456,8 +454,9 @@ function radio_edit($cgr) { } } else { // Add initial radio button - model['radio_text_' + radio_button_count] = - 'radio_text_' + radio_button_count; + model[ + `radio_text_${radio_button_count}` + ] = `radio_text_${radio_button_count}`; add_radio( $radio_group, $radio_value_spec, @@ -468,15 +467,16 @@ function radio_edit($cgr) { radio_button_count += 1; } - var radio_group_view = binding($edit, $radio_group, model, $done); + let radio_group_view = binding($edit, $radio_group, model, $done); $add_radio_button.unbind('click'); // Add functionality for adding and removing radio buttons in the group $add_radio_button.on('click', 'button', function(e) { e.preventDefault(); - model['radio_text_' + radio_button_count] = - 'radio_text_' + radio_button_count; + model[ + `radio_text_${radio_button_count}` + ] = `radio_text_${radio_button_count}`; add_radio( $radio_group, $radio_value_spec, @@ -495,39 +495,39 @@ function radio_edit($cgr) { function checkbox_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var userkey = ''; - var checkbox_text = 'checkbox text'; - var required = false; + let label = 'label'; + let userkey = ''; + let checkbox_text = 'checkbox text'; + let required = false; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); userkey = $('.controls input', $cgr).data('userkey'); checkbox_text = $('.controls label span', $cgr).text(); required = $('input', $cgr).prop('required'); } - var $checkbox = $(input.checkbox()); - var $buttons = $(input.button()); - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $checkbox_text = $(spec.checkbox_text()); - var $required = $(spec.required()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $checkbox = $(input.checkbox()); + const $buttons = $(input.button()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $checkbox_text = $(spec.checkbox_text()); + const $required = $(spec.required()); + const $done = $(spec.done()); + const $edit = $('
').append( $label, $userkey, $checkbox_text, $required, $done ); - var $new_cgr = $( + const $new_cgr = $( '
checkbox
' ).append($checkbox); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - checkbox_text: checkbox_text, - required: required, + const model = { + label, + userkey, + checkbox_text, + required, }; $('input', $label).val(label); $('input', $userkey).val(userkey); @@ -539,11 +539,11 @@ function checkbox_edit($cgr) { function text_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var userkey = ''; - var placeholder = ''; - var help = ''; - var required = false; + let label = 'label'; + let userkey = ''; + let placeholder = ''; + let help = ''; + let required = false; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); userkey = $('.controls input', $cgr).data('userkey'); @@ -551,15 +551,15 @@ function text_edit($cgr) { help = $('.controls span.help-block', $cgr).text(); required = $('.controls input', $cgr).prop('required'); } - var $text = $(input.text()); - var $buttons = $(input.button()); - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $placeholder = $(spec.placeholder()); - var $help = $(spec.help()); - var $required = $(spec.required()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $text = $(input.text()); + const $buttons = $(input.button()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $placeholder = $(spec.placeholder()); + const $help = $(spec.help()); + const $required = $(spec.required()); + const $done = $(spec.done()); + const $edit = $('
').append( $label, $userkey, $placeholder, @@ -567,16 +567,16 @@ function text_edit($cgr) { $required, $done ); - var $new_cgr = $( + const $new_cgr = $( '
text
' ).append($text); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - placeholder: placeholder, - help: help, - required: required, + const model = { + label, + userkey, + placeholder, + help, + required, }; $('input', $label).val(label); $('input', $userkey).val(userkey); @@ -588,31 +588,31 @@ function text_edit($cgr) { function figure_edit($cgr) { $('#output .well.spec').remove(); - var src = ''; - var alt = ''; - var figcaption = ''; - var width = ''; + let src = ''; + let alt = ''; + let figcaption = ''; + let width = ''; if ($cgr) { src = $('img', $cgr).attr('src'); alt = $('img', $cgr).attr('alt'); figcaption = $('figcaption', $cgr).text(); width = $('img', $cgr).attr('width') || $('img', $cgr).prop('clientWidth'); } - var $figure = $(input.figure()); - var $buttons = $(input.button()); - var $file = $(spec.imagefile()); - var $alt = $(spec.alt()); - var $figcaption = $(spec.figcaption()); - var $width = $(spec.width()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $figure = $(input.figure()); + const $buttons = $(input.button()); + const $file = $(spec.imagefile()); + const $alt = $(spec.alt()); + const $figcaption = $(spec.figcaption()); + const $width = $(spec.width()); + const $done = $(spec.done()); + const $edit = $('
').append( $file, $alt, $width, $figcaption, $done ); - var $new_cgr = $( + const $new_cgr = $( '
figure
' ).append($figure); add_new_cgr($cgr, $new_cgr, $buttons, $edit); @@ -624,7 +624,7 @@ function figure_edit($cgr) { $new_cgr.remove(); }); - var model; + let model; if ($cgr) { $('img', $figure).prop('src', src); // enable the spec inputs @@ -637,9 +637,9 @@ function figure_edit($cgr) { $('input', $width).val(width); model = { - alt: alt, - figcaption: figcaption, - width: width, + alt, + figcaption, + width, }; binding($edit, $figure, model, $done); @@ -648,13 +648,13 @@ function figure_edit($cgr) { // handle image upload here $('input:file', $file).change(function(e) { e.preventDefault(); - var file = this.files[0]; + const file = this.files[0]; if (file === undefined) { $file.children('.file-upload-buttons').remove(); return; } - var $validation = $file.find('.validation'); + let $validation = $file.find('.validation'); if ($validation.length) { $validation = $($validation[0]); } else { @@ -665,7 +665,7 @@ function figure_edit($cgr) { if (!/image\/(gif|jpe?g|png)$/i.test(file.type)) { $validation.html( - '

' + file.type + ' is not allowed to upload

' + `

${file.type} is not allowed to upload

` ); $file.children('.file-upload-buttons').remove(); return; @@ -673,7 +673,7 @@ function figure_edit($cgr) { if (file.size > 5000000) { $validation.html( - '

' + file.size + ' is too large to upload

' + `

${file.size} is too large to upload

` ); $file.children('.file-upload-buttons').remove(); return; @@ -692,10 +692,10 @@ function figure_edit($cgr) { $file.on('click', 'button[value="upload"]', function(e) { e.preventDefault(); // ajax to save the current value - var $this = $(this); + const $this = $(this); $this.attr('disabled', true); - var input = $file.find('input')[0]; - var data = new FormData(); + const input = $file.find('input')[0]; + const data = new FormData(); data.append('name', input.name); data.append('type', input.type); data.append(input.name, input.files[0]); @@ -704,15 +704,15 @@ function figure_edit($cgr) { type: 'POST', processData: false, contentType: false, // important for jqXHR - data: data, + data, }) .done(function(res, status, jqXHR) { - var location = jqXHR.getResponseHeader('Location'); - var timestamp = jqXHR.getResponseHeader('Date'); + const location = jqXHR.getResponseHeader('Location'); + const timestamp = jqXHR.getResponseHeader('Date'); $('#message').append( - '
File uploaded ' + - livespan(timestamp) + - '
' + `
File uploaded ${livespan( + timestamp + )}
` ); $.livestamp.resume(); @@ -736,9 +736,9 @@ function figure_edit($cgr) { $('input', $figcaption).val(figcaption); model = { - alt: alt, - figcaption: figcaption, - width: width, + alt, + figcaption, + width, }; binding($edit, $figure, model, $done); @@ -746,9 +746,8 @@ function figure_edit($cgr) { .fail(function(jqXHR) { if (jqXHR.status !== 401) { $('#message').append( - '
Cannot upload the file: ' + - (jqXHR.responseText || 'unknown') + - '
' + `
Cannot upload the file: ${jqXHR.responseText || + 'unknown'}
` ); $(window).scrollTop($('#message div:last-child').offset().top - 40); } @@ -766,12 +765,12 @@ function figure_edit($cgr) { function other_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var userkey = ''; - var placeholder = ''; - var help = ''; - var type = 'text'; - var required = false; + let label = 'label'; + let userkey = ''; + let placeholder = ''; + let help = ''; + let type = 'text'; + let required = false; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); userkey = $('.controls input', $cgr).data('userkey'); @@ -780,16 +779,16 @@ function other_edit($cgr) { help = $('.controls span.help-block', $cgr).text(); required = $('input', $cgr).prop('required'); } - var $other = $(input.other()); - var $buttons = $(input.button()); - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $placeholder = $(spec.placeholder()); - var $type = $(spec.type()); - var $help = $(spec.help()); - var $required = $(spec.required()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $other = $(input.other()); + const $buttons = $(input.button()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $placeholder = $(spec.placeholder()); + const $type = $(spec.type()); + const $help = $(spec.help()); + const $required = $(spec.required()); + const $done = $(spec.done()); + const $edit = $('
').append( $type, $label, $userkey, @@ -798,18 +797,18 @@ function other_edit($cgr) { $required, $done ); - var $new_cgr = $( + const $new_cgr = $( '
other
' ).append($other); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - placeholder: placeholder, - type: type, - help: help, - required: required, + const model = { + label, + userkey, + placeholder, + type, + help, + required, }; $('input', $label).val(label); $('input', $userkey).val(userkey); @@ -823,12 +822,12 @@ function other_edit($cgr) { function textarea_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var userkey = ''; - var placeholder = ''; - var rows = 3; - var help = ''; - var required = false; + let label = 'label'; + let userkey = ''; + let placeholder = ''; + let rows = 3; + let help = ''; + let required = false; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); @@ -839,16 +838,16 @@ function textarea_edit($cgr) { required = $('textarea', $cgr).prop('required'); } - var $textarea = $(input.textarea()); - var $buttons = $(input.button()); - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $placeholder = $(spec.placeholder()); - var $rows = $(spec.rows()); - var $help = $(spec.help()); - var $required = $(spec.required()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $textarea = $(input.textarea()); + const $buttons = $(input.button()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $placeholder = $(spec.placeholder()); + const $rows = $(spec.rows()); + const $help = $(spec.help()); + const $required = $(spec.required()); + const $done = $(spec.done()); + const $edit = $('
').append( $label, $userkey, $placeholder, @@ -857,18 +856,18 @@ function textarea_edit($cgr) { $required, $done ); - var $new_cgr = $( + const $new_cgr = $( '
textarea
' ).append($textarea); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - placeholder: placeholder, - rows: rows, - help: help, - required: required, + const model = { + label, + userkey, + placeholder, + rows, + help, + required, }; $('input', $label).val(label); @@ -882,26 +881,26 @@ function textarea_edit($cgr) { } function rangeText(min, max) { - var output = []; + const output = []; if (typeof min === 'number') { - output.push('min: ' + min); + output.push(`min: ${min}`); } if (typeof max === 'number') { - output.push('max: ' + max); + output.push(`max: ${max}`); } return output.join(', ') || null; } function number_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var userkey = ''; - var placeholder = ''; - var help = ''; - var required = false; - var min = null; - var max = null; - var range = null; + let label = 'label'; + let userkey = ''; + let placeholder = ''; + let help = ''; + let required = false; + let min = null; + let max = null; + let range = null; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); userkey = $('.controls input', $cgr).data('userkey'); @@ -913,17 +912,17 @@ function number_edit($cgr) { range = rangeText(min, max); } - var $number = $(input.number()); - var $buttons = $(input.button()); - var $label = $(spec.label()); - var $userkey = $(spec.userkey()); - var $placeholder = $(spec.placeholder()); - var $help = $(spec.help()); - var $min = $(spec.min()); - var $max = $(spec.max()); - var $required = $(spec.required()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $number = $(input.number()); + const $buttons = $(input.button()); + const $label = $(spec.label()); + const $userkey = $(spec.userkey()); + const $placeholder = $(spec.placeholder()); + const $help = $(spec.help()); + const $min = $(spec.min()); + const $max = $(spec.max()); + const $required = $(spec.required()); + const $done = $(spec.done()); + const $edit = $('
').append( $label, $userkey, $placeholder, @@ -933,20 +932,20 @@ function number_edit($cgr) { $required, $done ); - var $new_cgr = $( + const $new_cgr = $( '
number
' ).append($number); add_new_cgr($cgr, $new_cgr, $buttons, $edit); - var model = { - label: label, - userkey: userkey, - placeholder: placeholder, - help: help, - required: required, - min: min, - max: max, - range: range, + const model = { + label, + userkey, + placeholder, + help, + required, + min, + max, + range, }; $('input', $label).val(label); @@ -962,10 +961,10 @@ function number_edit($cgr) { function file_edit($cgr) { $('#output .well.spec').remove(); - var label = 'label'; - var required = false; - var userkey = ''; - var help = ''; + let label = 'label'; + let required = false; + let userkey = ''; + let help = ''; if ($cgr) { label = $('.control-label span.model-label', $cgr).text(); required = $('input', $cgr).prop('required'); @@ -973,20 +972,20 @@ function file_edit($cgr) { help = $('.controls span.help-block', $cgr).text(); } - var $upload = $(input.upload()); - var $label = $(spec.label()); - var $required = $(spec.required()); - var $userkey = $(spec.userkey()); - var $help = $(spec.help()); - var $done = $(spec.done()); - var $edit = $('
').append( + const $upload = $(input.upload()); + const $label = $(spec.label()); + const $required = $(spec.required()); + const $userkey = $(spec.userkey()); + const $help = $(spec.help()); + const $done = $(spec.done()); + const $edit = $('
').append( $label, $required, $userkey, $help, $done ); - var $new_cgr = $( + const $new_cgr = $( '
file
' ).append($upload); if ($cgr) { @@ -997,11 +996,11 @@ function file_edit($cgr) { $('#output').append($edit); } - var model = { - label: label, - required: required, - userkey: userkey, - help: help, + const model = { + label, + required, + userkey, + help, }; $('input', $label).val(label); @@ -1014,15 +1013,15 @@ function file_edit($cgr) { function section_edit($cgr) { $('#output .well.spec').remove(); - var legend = 'Section name'; + let legend = 'Section name'; if ($cgr) { legend = $('legend span.label-text', $cgr).text(); } - var $section = $(input.section()); - var $legend = $(spec.legend()); - var $done = $(spec.done()); - var $edit = $('
').append($legend, $done); - var $new_cgr = $( + const $section = $(input.section()); + const $legend = $(spec.legend()); + const $done = $(spec.done()); + const $edit = $('
').append($legend, $done); + const $new_cgr = $( '
section
' ).append($section); if ($cgr) { @@ -1032,8 +1031,8 @@ function section_edit($cgr) { $('#output').append($new_cgr); $('#output').append($edit); } - var model = { - legend: legend, + const model = { + legend, }; $('input', $legend).val(legend); @@ -1043,15 +1042,18 @@ function section_edit($cgr) { function rich_edit($cgr) { $('#output .well.spec').remove(); - var html = ''; + let html = ''; if ($cgr) { html = $('.tinymce', $cgr).html(); } - var $rich = $(input.rich()); - var $rich_textarea = $(spec.rich_textarea()); - var $done = $(spec.done()); - var $edit = $('
').append($rich_textarea, $done); - var $new_cgr = $( + const $rich = $(input.rich()); + const $rich_textarea = $(spec.rich_textarea()); + const $done = $(spec.done()); + const $edit = $('
').append( + $rich_textarea, + $done + ); + const $new_cgr = $( '
rich
' ).append($rich); if ($cgr) { @@ -1066,7 +1068,7 @@ function rich_edit($cgr) { tinymce.init(mce_content); $done.click(function(e) { e.preventDefault(); - var content = tinymce.activeEditor.getContent(); + const content = tinymce.activeEditor.getContent(); if (content === '') { // nothing was done tinymce.remove(); @@ -1079,7 +1081,7 @@ function rich_edit($cgr) { .closest('.spec') .remove(); $rich.closest('.control-group-wrap').removeAttr('data-status'); - var resultParent = $rich[0]; + const resultParent = $rich[0]; addSectionNumberToRichInstruction(resultParent); updateSectionNumbers(); } @@ -1090,30 +1092,34 @@ function init() { $('#output') .find('img') .each(function() { - var $this = $(this); + const $this = $(this); if ($this.attr('name')) { if ($this.attr('src') === undefined) { - $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + $($this.attr('src', `${prefix}/formfiles/${$this.attr('name')}`)); return; } if ($this.attr('src').indexOf('http') === 0) { - $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + $($this.attr('src', `${prefix}/formfiles/${$this.attr('name')}`)); return; } if (prefix && $this.attr('src').indexOf(prefix) !== 0) { - $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); - return; + $($this.attr('src', `${prefix}/formfiles/${$this.attr('name')}`)); } } }); initHtml = $('#output').html(); + $('span.time').each(function() { + $(this).text( + moment($(this).text()).format('dddd, MMMM Do YYYY, h:mm:ss a') + ); + }); // update every 30 seconds - $.livestamp.interval(30 * 1000); + // $.livestamp.interval(30 * 1000); - rivets.binders['required'] = function(el, value) { - var attrToSet = 'required'; + rivets.binders.required = function(el, value) { + const attrToSet = 'required'; if (value) { el.setAttribute(attrToSet, value); @@ -1125,7 +1131,7 @@ function init() { } function scrollToBottom() { - var scrollingElement = document.scrollingElement || document.body; + const scrollingElement = document.scrollingElement || document.body; scrollingElement.scrollTop = scrollingElement.scrollHeight; } @@ -1223,7 +1229,7 @@ function binding_events() { $('#more').attr('disabled', true); $('#output').sortable({ placeholder: 'ui-state-highlight', - update: function() { + update() { updateSectionNumbers(); }, }); @@ -1259,7 +1265,7 @@ function binding_events() { '.control-focus a.btn.btn-warning[title="remove"]', function(e) { e.preventDefault(); - var $cgr = $(this).closest('.control-group-wrap'); + const $cgr = $(this).closest('.control-group-wrap'); if ($('.control-group-wrap[data-status="editing"]').length) { modalAlert( 'Finish editing first', @@ -1276,8 +1282,8 @@ function binding_events() { e ) { e.preventDefault(); - var that = this; - var $cgr = $(this).closest('.control-group-wrap'); + const that = this; + const $cgr = $(this).closest('.control-group-wrap'); if ($('.control-group-wrap[data-status="editing"]').length) { modalAlert( 'Finish editing first', @@ -1285,7 +1291,7 @@ function binding_events() { ); return; } - var cloned = $cgr.clone(); + const cloned = $cgr.clone(); $('.control-group-buttons', $(cloned)).remove(); $(cloned).removeClass('control-focus'); $('input, textarea', $(cloned)).attr('name', UID.generateShort()); @@ -1299,7 +1305,7 @@ function binding_events() { $('#output').on('click', '.control-focus a.btn[title="edit"]', function(e) { e.preventDefault(); - var $cgr = $(this).closest('.control-group-wrap'); + const $cgr = $(this).closest('.control-group-wrap'); if ( $('.control-group-wrap[data-status="editing"]').length && $cgr.attr('data-status') !== 'editing' @@ -1314,7 +1320,7 @@ function binding_events() { // modalAlert('You are still editing it', ''); return; } - var type = $('span.fe-type', $cgr).text(); + const type = $('span.fe-type', $cgr).text(); switch (type) { case 'rich': rich_edit($cgr); @@ -1361,12 +1367,12 @@ function binding_events() { return; } cleanBeforeSave(); - var html = $('#output').html(); + const html = $('#output').html(); // var path = window.location.pathname; if (html !== initHtml) { sendRequest( { - html: html, + html, }, function() { window.location.reload(true); @@ -1399,14 +1405,13 @@ function binding_events() { return; } cleanBeforeSave(); - var html = $('#output').html(); + const html = $('#output').html(); if (html !== initHtml) { e.preventDefault(); modalAlert( 'Save changes first', 'The form has been changed. Please save it before this action.' ); - return; } }); @@ -1417,7 +1422,7 @@ function binding_events() { return; } cleanBeforeSave(); - var html = $('#output').html(); + const html = $('#output').html(); $('#modalLabel').html('Save the form as (a new one)'); $('#modal .modal-body').empty(); $('#modal .modal-body').append( @@ -1428,12 +1433,12 @@ function binding_events() { ); $('#modal').modal('show'); $('#modal button[value="confirm"]').click(function() { - var title = $('#new-title').val(); + const title = $('#new-title').val(); sendRequest( { - html: html, - title: title, - formType: formType, + html, + title, + formType, }, null, 'saveas' @@ -1451,15 +1456,16 @@ function binding_events() { return; } cleanBeforeSave(); - var html = $('#output').html(); + const html = $('#output').html(); // var path = window.location.pathname; if (html !== initHtml) { modalAlert( 'Save before submit', - 'There are unsaved changes. Please save the changes if needed before submit.' + 'There are unsaved changes. Please save the changes if needed before submit for review.' ); return; } + sendRequest( { status: 0.5, @@ -1474,28 +1480,28 @@ function binding_events() { $('#release').click(function() { $('#modal .modal-body').empty(); - let defaultTitle = $('#formtitle').text(); + const defaultTitle = $('#formtitle').text(); $('#modal .modal-body').append( - '
' + `
` ); let priorVersionsTable = null; + let discrepancyTable; if (released_form_version_mgmt) { $('#modalLabel').html('Archive previously released form(s)'); $('#modal .modal-body').append( '

Prior version(s) of this form:

' ); - let priorVersionsColumns = [ + const priorVersionsColumns = [ selectColumn, titleColumn, releasedOnColumn, releasedByColumn, + releasedFormVersionColumn, releasedFormLinkColumn, ]; priorVersionsTable = $('#prior_versions').dataTable({ - sAjaxSource: '/forms/' + id + '/released/json', + sAjaxSource: `/forms/${id}/released/json`, sAjaxDataProp: '', bProcessing: true, oLanguage: { @@ -1504,6 +1510,11 @@ function binding_events() { aoColumns: priorVersionsColumns, iDisplayLength: 2, sDom: sDomPage, + fnDrawCallback() { + Holder.run({ + images: 'img.user', + }); + }, }); selectMultiEvent(priorVersionsTable); filterEvent(); @@ -1513,7 +1524,7 @@ function binding_events() { $('#modal .modal-body').append( '

Choose a discrepancy to attach

' ); - var discrepancyColumns = [ + const discrepancyColumns = [ selectColumn, titleColumn, versionColumn, @@ -1522,11 +1533,11 @@ function binding_events() { releasedFormLinkColumn, ]; fnAddFilterFoot('#discrepancy', discrepancyColumns); - var discrepancyTable = $('#discrepancy').dataTable({ + discrepancyTable = $('#discrepancy').dataTable({ sAjaxSource: '/released-forms/discrepancy/json', sAjaxDataProp: '', bProcessing: true, - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -1549,32 +1560,32 @@ function binding_events() { $('#modal button[value="confirm"]').click(function() { if (priorVersionsTable) { // get all selected forms - let selected = fnGetSelectedInPage( + const selected = fnGetSelectedInPage( priorVersionsTable, 'row-selected', false ); - let json = []; + const target = {}; $(selected).each(function(s) { const data = priorVersionsTable.fnGetData(s); - json.push(data._id); + target[data._id] = { version: data.ver, status: 2 }; }); - archive_prior_released_forms(json); + archive_prior_released_forms(target); } - var title = $('#release-title').val(); - var json = { - title: title, + const title = $('#release-title').val(); + const json = { + title, }; if (discrepancyTable) { // get only current page after filtered - var selected = fnGetSelectedInPage( + const selected = fnGetSelectedInPage( discrepancyTable, 'row-selected', true ); if (selected.length === 1) { - var data = discrepancyTable.fnGetData(selected[0]); + const data = discrepancyTable.fnGetData(selected[0]); json.discrepancyFormId = data._id; } } @@ -1583,16 +1594,18 @@ function binding_events() { }); }); - $('#reject').click(function() { + $('#submit-review').on('click', function(e) { + e.preventDefault(); sendRequest( { - status: 0, - version: Number($('#version').text()), + result: $('input[name="result"]:checked').val(), + comment: $('#comment').val(), + v: $('#version').text(), }, function() { window.location.reload(true); }, - 'status' + 'review' ); }); @@ -1616,7 +1629,7 @@ $(function() { disableAjaxCache(); init(); - if (formStatus === 0) { + if (formStatus === 0 || formStatus === 0.5) { working(); } binding_events(); diff --git a/public/javascripts/form-management.js b/public/javascripts/form-management.js index a4892b01..a842b0e5 100644 --- a/public/javascripts/form-management.js +++ b/public/javascripts/form-management.js @@ -1,7 +1,7 @@ /* global moment, ajax401, disableAjaxCache, prefix, updateAjaxURL, Holder, selectColumn, formLinkColumn, formConfigLinkColumn, titleColumn, tagsColumn, -keysColumn, updatedOnColumn, updatedByColumn, fnAddFilterFoot, sDomNoTools, +keysColumn, fnAddFilterFoot, sDomNoTools, reviewersColumn, firstReviewRequestedOnColumn, fnGetSelected, selectEvent, filterEvent, formShareLinkColumn, formStatusColumn, formTypeColumn, versionColumn, releasedFormStatusColumn, releasedFormVersionColumn, releasedByColumn, releasedOnColumn, @@ -11,12 +11,12 @@ archivedByColumn, archivedOnColumn, releasedFormLinkColumn function cloneFromModal(formTable) { $('#submit').prop('disabled', true); $('#return').prop('disabled', true); - var number = $('#modal .modal-body div.target').length; + let number = $('#modal .modal-body div.target').length; $('#modal .modal-body div.target').each(function() { - var that = this; - var success = false; + const that = this; + let success = false; $.ajax({ - url: '/forms/' + that.id + '/clone', + url: `/forms/${that.id}/clone`, type: 'POST', }) .done(function() { @@ -26,7 +26,7 @@ function cloneFromModal(formTable) { }) .fail(function(jqXHR) { $(that).prepend(''); - $(that).append(' : ' + jqXHR.responseText); + $(that).append(` : ${jqXHR.responseText}`); $(that).addClass('text-error'); }) .always(function() { @@ -41,28 +41,23 @@ function cloneFromModal(formTable) { function showHash() { if (window.location.hash) { - $('.nav-tabs a[href=' + window.location.hash + ']').tab('show'); + $(`.nav-tabs a[href=${window.location.hash}]`).tab('show'); } } function formatItemUpdate(data) { - return ( - '
' + - data.title + - ', created ' + - moment(data.createdOn).fromNow() + - (data.updatedOn ? ', updated ' + moment(data.updatedOn).fromNow() : '') + - '
' - ); + return `
${ + data.title + }, created ${moment(data.createdOn).fromNow()}${ + data.updatedOn ? `, updated ${moment(data.updatedOn).fromNow()}` : '' + }
`; } $(function() { ajax401(prefix); updateAjaxURL(prefix); disableAjaxCache(); - var formAoColumns = [ + const formAoColumns = [ selectColumn, formLinkColumn, formConfigLinkColumn, @@ -73,13 +68,13 @@ $(function() { formStatusColumn, tagsColumn, keysColumn, - updatedByColumn, - updatedOnColumn, + reviewersColumn, + firstReviewRequestedOnColumn, ]; - var formTable = $('#submitted-form-table').dataTable({ + const formTable = $('#submitted-form-table').dataTable({ sAjaxSource: '/submitted-forms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -87,7 +82,10 @@ $(function() { bAutoWidth: false, bProcessing: true, iDisplayLength: 10, - aLengthMenu: [[10, 50, 100, -1], [10, 50, 100, 'All']], + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], oLanguage: { sLoadingRecords: 'Please wait - loading data from the server ...', }, @@ -98,7 +96,7 @@ $(function() { }); fnAddFilterFoot('#submitted-form-table', formAoColumns); - var releasedFormAoColumns = [ + const releasedFormAoColumns = [ selectColumn, releasedFormLinkColumn, titleColumn, @@ -109,10 +107,10 @@ $(function() { releasedByColumn, releasedOnColumn, ]; - var releasedFormTable = $('#released-form-table').dataTable({ + const releasedFormTable = $('#released-form-table').dataTable({ sAjaxSource: '/released-forms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -120,7 +118,10 @@ $(function() { bAutoWidth: false, bProcessing: true, iDisplayLength: 10, - aLengthMenu: [[10, 50, 100, -1], [10, 50, 100, 'All']], + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], oLanguage: { sLoadingRecords: 'Please wait - loading data from the server ...', }, @@ -131,7 +132,7 @@ $(function() { }); fnAddFilterFoot('#released-form-table', releasedFormAoColumns); - var archivedFormAoColumns = [ + const archivedFormAoColumns = [ selectColumn, formLinkColumn, titleColumn, @@ -142,10 +143,10 @@ $(function() { archivedByColumn, archivedOnColumn, ]; - var archivedFormTable = $('#archived-form-table').dataTable({ + const archivedFormTable = $('#archived-form-table').dataTable({ sAjaxSource: '/archived-released-forms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -153,7 +154,10 @@ $(function() { bAutoWidth: false, bProcessing: true, iDisplayLength: 10, - aLengthMenu: [[10, 50, 100, -1], [10, 50, 100, 'All']], + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], oLanguage: { sLoadingRecords: 'Please wait - loading data from the server ...', }, @@ -169,7 +173,7 @@ $(function() { // add state for tab changes $('.nav-tabs a').on('click', function() { - window.history.pushState(null, 'forms :: ' + this.text, this.href); + window.history.pushState(null, `forms :: ${this.text}`, this.href); }); // show the tab when back and forward @@ -178,8 +182,8 @@ $(function() { }; $('#clone').click(function() { - var activeTable = $('.tab-pane.active table').dataTable(); - var selected = fnGetSelected(activeTable, 'row-selected'); + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); if (selected.length === 0) { $('#modalLabel').html('Alert'); $('#modal .modal-body').html('No form has been selected!'); @@ -188,12 +192,10 @@ $(function() { ); $('#modal').modal('show'); } else { - $('#modalLabel').html( - 'Clone the following ' + selected.length + ' forms? ' - ); + $('#modalLabel').html(`Clone the following ${selected.length} forms? `); $('#modal .modal-body').empty(); selected.forEach(function(row) { - var data = activeTable.fnGetData(row); + const data = activeTable.fnGetData(row); $('#modal .modal-body').append(formatItemUpdate(data)); }); $('#modal .modal-footer').html( diff --git a/public/javascripts/forms.js b/public/javascripts/forms.js index cf88d733..15c433b6 100644 --- a/public/javascripts/forms.js +++ b/public/javascripts/forms.js @@ -6,14 +6,14 @@ transferredOnColumn, ownerColumn, formStatusColumn, formTypeColumn, versionColumn, releasedFormLinkColumn, releasedFormStatusColumn, releasedFormVersionColumn, releasedByColumn, releasedOnColumn, - transferFromModal, archivedByColumn, archivedOnColumn */ + transferFromModal, archivedByColumn, archivedOnColumn, formReviewLinkColumn */ function travelFromModal() { $('#submit').prop('disabled', true); $('#return').prop('disabled', true); - var number = $('#modal .modal-body div.target').length; + let number = $('#modal .modal-body div.target').length; $('#modal .modal-body div.target').each(function() { - var that = this; + const that = this; $.ajax({ url: '/travelers/', type: 'POST', @@ -25,11 +25,10 @@ function travelFromModal() { .done(function() { $(that).prepend(''); $(that).addClass('text-success'); - // success = true; }) .fail(function(jqXHR) { $(that).prepend(''); - $(that).append(' : ' + jqXHR.responseText); + $(that).append(` : ${jqXHR.responseText}`); $(that).addClass('text-error'); }) .always(function() { @@ -44,12 +43,13 @@ function travelFromModal() { function cloneFromModal(activeTable, formTable) { $('#submit').prop('disabled', true); $('#return').prop('disabled', true); - var number = $('#modal .modal-body div.target').length; - var base = activeTable.fnSettings()['sAjaxSource'].split('/')[1]; + let number = $('#modal .modal-body div.target').length; + let base = activeTable.fnSettings().sAjaxSource.split('/')[1]; if ( base === 'archivedforms' || base === 'sharedforms' || - base === 'groupsharedforms' + base === 'groupsharedforms' || + base === 'closedforms' ) { base = 'forms'; } @@ -58,10 +58,10 @@ function cloneFromModal(activeTable, formTable) { base = 'released-forms'; } $('#modal .modal-body div.target').each(function() { - var that = this; - var success = false; + const that = this; + let success = false; $.ajax({ - url: '/' + base + '/' + that.id + '/clone', + url: `/${base}/${that.id}/clone`, type: 'POST', contentType: 'application/json', data: JSON.stringify({ @@ -75,7 +75,7 @@ function cloneFromModal(activeTable, formTable) { }) .fail(function(jqXHR) { $(that).prepend(''); - $(that).append(' : ' + jqXHR.responseText); + $(that).append(` : ${jqXHR.responseText}`); $(that).addClass('text-error'); }) .always(function() { @@ -90,34 +90,24 @@ function cloneFromModal(activeTable, formTable) { function showHash() { if (window.location.hash) { - $('.nav-tabs a[href=' + window.location.hash + ']').tab('show'); + $(`.nav-tabs a[href=${window.location.hash}]`).tab('show'); } } function formatItemUpdate(data) { - return ( - '
' + data.title + '
' - ); + return `
${data.title}
`; } function cloneItem(data) { - return ( - '
clone ' + - data.title + - '
with new title:
' - ); + return `
clone ${data.title}
with new title:
`; } $(function() { ajax401(prefix); updateAjaxURL(prefix); disableAjaxCache(); - /*form table starts*/ - var formAoColumns = [ + /* form table starts */ + const formAoColumns = [ selectColumn, formLinkColumn, formConfigLinkColumn, @@ -134,10 +124,10 @@ $(function() { sharedWithColumn, sharedGroupColumn, ]; - var formTable = $('#form-table').dataTable({ + const formTable = $('#form-table').dataTable({ sAjaxSource: '/forms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -161,10 +151,10 @@ $(function() { sDom: sDomNoTools, }); fnAddFilterFoot('#form-table', formAoColumns); - /*form table ends*/ + /* form table ends */ - /*transferred form table starts*/ - var transferredFormAoColumns = [ + /* transferred form table starts */ + const transferredFormAoColumns = [ selectColumn, formLinkColumn, formShareLinkColumn, @@ -182,10 +172,10 @@ $(function() { sharedWithColumn, sharedGroupColumn, ]; - var transferredFormTable = $('#transferred-form-table').dataTable({ + const transferredFormTable = $('#transferred-form-table').dataTable({ sAjaxSource: '/transferredforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -209,10 +199,10 @@ $(function() { sDom: sDomNoTools, }); fnAddFilterFoot('#transferred-form-table', transferredFormAoColumns); - /*transferred form table ends*/ + /* transferred form table ends */ - /*shared form table starts*/ - var sharedFormAoColumns = [ + /* shared form table starts */ + const sharedFormAoColumns = [ selectColumn, formLinkColumn, titleColumn, @@ -227,10 +217,10 @@ $(function() { sharedWithColumn, sharedGroupColumn, ]; - var sharedFormTable = $('#shared-form-table').dataTable({ + const sharedFormTable = $('#shared-form-table').dataTable({ sAjaxSource: '/sharedforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -251,14 +241,14 @@ $(function() { sDom: sDomNoTools, }); fnAddFilterFoot('#shared-form-table', sharedFormAoColumns); - /*shared form table ends*/ + /* shared form table ends */ - /*group shared form table starts*/ - var groupSharedFormAoColumns = sharedFormAoColumns; - var groupSharedFormTable = $('#group-shared-form-table').dataTable({ + /* group shared form table starts */ + const groupSharedFormAoColumns = sharedFormAoColumns; + const groupSharedFormTable = $('#group-shared-form-table').dataTable({ sAjaxSource: '/groupsharedforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -279,24 +269,30 @@ $(function() { sDom: sDomNoTools, }); fnAddFilterFoot('#group-shared-form-table', groupSharedFormAoColumns); - /*group shared form table ends*/ + /* group shared form table ends */ - /*released form table starts*/ - var releasedFormAoColumns = [ + /* submitted form table starts */ + const submittedFormAoColumns = [ selectColumn, - releasedFormLinkColumn, + formLinkColumn, + formConfigLinkColumn, + formReviewLinkColumn, titleColumn, - releasedFormStatusColumn, + formStatusColumn, formTypeColumn, - releasedFormVersionColumn, + versionColumn, tagsColumn, - releasedByColumn, - releasedOnColumn, + keysColumn, + createdOnColumn, + updatedOnColumn, + updatedByColumn, + sharedWithColumn, + sharedGroupColumn, ]; - var releasedFormTable = $('#released-form-table').dataTable({ - sAjaxSource: '/released-forms/json', + const submittedFormTable = $('#submitted-form-table').dataTable({ + sAjaxSource: '/submittedforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -312,29 +308,38 @@ $(function() { sLoadingRecords: 'Please wait - loading data from the server ...', }, bDeferRender: true, - aoColumns: releasedFormAoColumns, - aaSorting: [[8, 'desc']], + aoColumns: submittedFormAoColumns, + aaSorting: [ + [11, 'desc'], + [10, 'desc'], + ], sDom: sDomNoTools, }); - fnAddFilterFoot('#released-form-table', releasedFormAoColumns); - /*released form table ends*/ + fnAddFilterFoot('#submitted-form-table', submittedFormAoColumns); + /* submitted form table ends */ - /*archieved form table starts*/ - var archivedFormAoColumns = [ + /* closed form table starts */ + const closedFormAoColumns = [ selectColumn, formLinkColumn, + formConfigLinkColumn, + formReviewLinkColumn, titleColumn, + formStatusColumn, formTypeColumn, versionColumn, tagsColumn, keysColumn, - updatedByColumn, + createdOnColumn, updatedOnColumn, + updatedByColumn, + sharedWithColumn, + sharedGroupColumn, ]; - var archivedFormTable = $('#archived-form-table').dataTable({ - sAjaxSource: '/archivedforms/json', + const closedFormTable = $('#closed-form-table').dataTable({ + sAjaxSource: '/closedforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -350,27 +355,32 @@ $(function() { sLoadingRecords: 'Please wait - loading data from the server ...', }, bDeferRender: true, - aoColumns: archivedFormAoColumns, - aaSorting: [[8, 'desc']], + aoColumns: closedFormAoColumns, + aaSorting: [ + [11, 'desc'], + [10, 'desc'], + ], sDom: sDomNoTools, }); - fnAddFilterFoot('#archived-form-table', archivedFormAoColumns); - /*archived form table ends*/ + fnAddFilterFoot('#closed-form-table', closedFormAoColumns); + /* submitted form table ends */ - var archivedReleasedFormAoColumns = [ + /* archieved form table starts */ + const archivedFormAoColumns = [ selectColumn, - releasedFormLinkColumn, + formLinkColumn, titleColumn, formTypeColumn, + versionColumn, tagsColumn, - releasedFormVersionColumn, - archivedByColumn, - archivedOnColumn, + keysColumn, + updatedByColumn, + updatedOnColumn, ]; - var archivedReleasedFormTable = $('#archived-released-form-table').dataTable({ - sAjaxSource: '/archived-released-forms/json', + const archivedFormTable = $('#archived-form-table').dataTable({ + sAjaxSource: '/archivedforms/json', sAjaxDataProp: '', - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -386,21 +396,19 @@ $(function() { sLoadingRecords: 'Please wait - loading data from the server ...', }, bDeferRender: true, - aoColumns: archivedReleasedFormAoColumns, - aaSorting: [[7, 'desc']], + aoColumns: archivedFormAoColumns, + aaSorting: [[8, 'desc']], sDom: sDomNoTools, }); - fnAddFilterFoot( - '#archived-released-form-table', - archivedReleasedFormAoColumns - ); + fnAddFilterFoot('#archived-form-table', archivedFormAoColumns); + /* archived form table ends */ // show the tab in hash showHash(); // add state for tab changes $('.nav-tabs a').on('click', function() { - window.history.pushState(null, 'forms :: ' + this.text, this.href); + window.history.pushState(null, `forms :: ${this.text}`, this.href); }); // show the tab when back and forward @@ -409,8 +417,8 @@ $(function() { }; $('#form-travel').click(function() { - var activeTable = $('.tab-pane.active table').dataTable(); - var selected = fnGetSelected(activeTable, 'row-selected'); + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); if (selected.length === 0) { $('#modalLabel').html('Alert'); $('#modal .modal-body').html('No form has been selected!'); @@ -420,11 +428,11 @@ $(function() { $('#modal').modal('show'); } else { $('#modalLabel').html( - 'Create travelers from the following ' + selected.length + ' forms? ' + `Create travelers from the following ${selected.length} forms? ` ); $('#modal .modal-body').empty(); selected.forEach(function(row) { - var data = activeTable.fnGetData(row); + const data = activeTable.fnGetData(row); $('#modal .modal-body').append(formatItemUpdate(data)); }); $('#modal .modal-footer').html( @@ -438,8 +446,8 @@ $(function() { }); $('button.transfer').click(function() { - var activeTable = $('.tab-pane.active table').dataTable(); - var selected = fnGetSelected(activeTable, 'row-selected'); + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); if (selected.length === 0) { $('#modalLabel').html('Alert'); $('#modal .modal-body').html('No form has been selected!'); @@ -449,11 +457,11 @@ $(function() { $('#modal').modal('show'); } else { $('#modalLabel').html( - 'Transfer the following ' + selected.length + ' forms? ' + `Transfer the following ${selected.length} forms? ` ); $('#modal .modal-body').empty(); selected.forEach(function(row) { - var data = activeTable.fnGetData(row); + const data = activeTable.fnGetData(row); $('#modal .modal-body').append(formatItemUpdate(data)); }); $('#modal .modal-body').append('
to the following user
'); @@ -486,8 +494,8 @@ $(function() { }); $('#clone').click(function() { - var activeTable = $('.tab-pane.active table').dataTable(); - var selected = fnGetSelected(activeTable, 'row-selected'); + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); if (selected.length === 0) { $('#modalLabel').html('Alert'); $('#modal .modal-body').html('No form has been selected!'); @@ -496,12 +504,10 @@ $(function() { ); $('#modal').modal('show'); } else { - $('#modalLabel').html( - 'Clone the following ' + selected.length + ' form(s)? ' - ); + $('#modalLabel').html(`Clone the following ${selected.length} form(s)? `); $('#modal .modal-body').empty(); selected.forEach(function(row) { - var data = activeTable.fnGetData(row); + const data = activeTable.fnGetData(row); $('#modal .modal-body').append(cloneItem(data)); }); $('#modal .modal-footer').html( @@ -519,9 +525,9 @@ $(function() { transferredFormTable.fnReloadAjax(); sharedFormTable.fnReloadAjax(); groupSharedFormTable.fnReloadAjax(); - releasedFormTable.fnReloadAjax(); + submittedFormTable.fnReloadAjax(); + closedFormTable.fnReloadAjax(); archivedFormTable.fnReloadAjax(); - archivedReleasedFormTable.fnReloadAjax(); }); // binding events selectEvent(); diff --git a/public/javascripts/released-forms.js b/public/javascripts/released-forms.js new file mode 100644 index 00000000..6635fd37 --- /dev/null +++ b/public/javascripts/released-forms.js @@ -0,0 +1,312 @@ +/* global ajax401, disableAjaxCache, prefix, updateAjaxURL, + travelerGlobal, Holder, selectColumn, formLinkColumn, formConfigLinkColumn, titleColumn, tagsColumn, keysColumn, createdOnColumn, + updatedOnColumn, updatedByColumn, sharedWithColumn, sharedGroupColumn, + fnAddFilterFoot, sDomNoTools, createdByColumn, createdOnColumn, + fnGetSelected, selectEvent, filterEvent, formShareLinkColumn, + transferredOnColumn, ownerColumn, formStatusColumn, formTypeColumn, + versionColumn, releasedFormLinkColumn, releasedFormStatusColumn, + releasedFormVersionColumn, releasedByColumn, releasedOnColumn, + transferFromModal, archivedByColumn, archivedOnColumn, formReviewLinkColumn */ + +function travelFromModal() { + $('#submit').prop('disabled', true); + $('#return').prop('disabled', true); + let number = $('#modal .modal-body div.target').length; + $('#modal .modal-body div.target').each(function() { + const that = this; + $.ajax({ + url: '/travelers/', + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + form: this.id, + }), + }) + .done(function() { + $(that).prepend(''); + $(that).addClass('text-success'); + }) + .fail(function(jqXHR) { + $(that).prepend(''); + $(that).append(` : ${jqXHR.responseText}`); + $(that).addClass('text-error'); + }) + .always(function() { + number = number - 1; + if (number === 0) { + $('#return').prop('disabled', false); + } + }); + }); +} + +function cloneFromModal(activeTable) { + $('#submit').prop('disabled', true); + $('#return').prop('disabled', true); + let number = $('#modal .modal-body div.target').length; + let base = activeTable.fnSettings().sAjaxSource.split('/')[1]; + if ( + base === 'archivedforms' || + base === 'sharedforms' || + base === 'groupsharedforms' + ) { + base = 'forms'; + } + + if (base === 'archived-released-forms') { + base = 'released-forms'; + } + $('#modal .modal-body div.target').each(function() { + const that = this; + let success = false; + $.ajax({ + url: `/${base}/${that.id}/clone`, + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + title: $('input', $(that)).val(), + }), + }) + .done(function() { + $(that).prepend(''); + $(that).addClass('text-success'); + success = true; + }) + .fail(function(jqXHR) { + $(that).prepend(''); + $(that).append(` : ${jqXHR.responseText}`); + $(that).addClass('text-error'); + }) + .always(function() { + number = number - 1; + if (number === 0 && success) { + $('#return').prop('disabled', false); + } + }); + }); +} + +function showHash() { + if (window.location.hash) { + $(`.nav-tabs a[href=${window.location.hash}]`).tab('show'); + } +} + +function formatItemUpdate(data) { + return `
${data.title}
`; +} + +function cloneItem(data) { + return `
clone ${data.title}
with new title:
`; +} + +$(function() { + ajax401(prefix); + updateAjaxURL(prefix); + disableAjaxCache(); + + /* released form table starts */ + const releasedFormAoColumns = [ + selectColumn, + releasedFormLinkColumn, + titleColumn, + releasedFormStatusColumn, + formTypeColumn, + releasedFormVersionColumn, + tagsColumn, + releasedByColumn, + releasedOnColumn, + ]; + const releasedFormTable = $('#released-form-table').dataTable({ + sAjaxSource: '/released-forms/json', + sAjaxDataProp: '', + fnDrawCallback() { + Holder.run({ + images: 'img.user', + }); + }, + bAutoWidth: false, + bProcessing: true, + iDisplayLength: 10, + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], + oLanguage: { + sLoadingRecords: 'Please wait - loading data from the server ...', + }, + bDeferRender: true, + aoColumns: releasedFormAoColumns, + aaSorting: [[8, 'desc']], + sDom: sDomNoTools, + }); + fnAddFilterFoot('#released-form-table', releasedFormAoColumns); + /* released form table ends */ + + /* archived released form table starts */ + const archivedReleasedFormAoColumns = [ + selectColumn, + releasedFormLinkColumn, + titleColumn, + formTypeColumn, + tagsColumn, + releasedFormVersionColumn, + archivedByColumn, + archivedOnColumn, + ]; + const archivedReleasedFormTable = $( + '#archived-released-form-table' + ).dataTable({ + sAjaxSource: '/archived-released-forms/json', + sAjaxDataProp: '', + fnDrawCallback() { + Holder.run({ + images: 'img.user', + }); + }, + bAutoWidth: false, + bProcessing: true, + iDisplayLength: 10, + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], + oLanguage: { + sLoadingRecords: 'Please wait - loading data from the server ...', + }, + bDeferRender: true, + aoColumns: archivedReleasedFormAoColumns, + aaSorting: [[7, 'desc']], + sDom: sDomNoTools, + }); + fnAddFilterFoot( + '#archived-released-form-table', + archivedReleasedFormAoColumns + ); + /* archived released form table ends */ + + // show the tab in hash + showHash(); + + // add state for tab changes + $('.nav-tabs a').on('click', function() { + window.history.pushState(null, `forms :: ${this.text}`, this.href); + }); + + // show the tab when back and forward + window.onhashchange = function() { + showHash(); + }; + + $('#form-travel').click(function() { + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); + if (selected.length === 0) { + $('#modalLabel').html('Alert'); + $('#modal .modal-body').html('No form has been selected!'); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + } else { + $('#modalLabel').html( + `Create travelers from the following ${selected.length} forms? ` + ); + $('#modal .modal-body').empty(); + selected.forEach(function(row) { + const data = activeTable.fnGetData(row); + $('#modal .modal-body').append(formatItemUpdate(data)); + }); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + $('#submit').click(function() { + travelFromModal(); + }); + } + }); + + $('button.transfer').click(function() { + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); + if (selected.length === 0) { + $('#modalLabel').html('Alert'); + $('#modal .modal-body').html('No form has been selected!'); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + } else { + $('#modalLabel').html( + `Transfer the following ${selected.length} forms? ` + ); + $('#modal .modal-body').empty(); + selected.forEach(function(row) { + const data = activeTable.fnGetData(row); + $('#modal .modal-body').append(formatItemUpdate(data)); + }); + $('#modal .modal-body').append('
to the following user
'); + $('#modal .modal-body').append( + '
' + ); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + + travelerGlobal.usernames.initialize(); + $('#username').typeahead( + { + minLength: 1, + highlight: true, + hint: true, + }, + { + name: 'usernames', + display: 'displayName', + limit: 20, + source: travelerGlobal.usernames, + } + ); + $('#submit').click(function() { + transferFromModal($('#username').val(), 'forms', activeTable); + }); + } + }); + + $('#clone').click(function() { + const activeTable = $('.tab-pane.active table').dataTable(); + const selected = fnGetSelected(activeTable, 'row-selected'); + if (selected.length === 0) { + $('#modalLabel').html('Alert'); + $('#modal .modal-body').html('No form has been selected!'); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + } else { + $('#modalLabel').html(`Clone the following ${selected.length} form(s)? `); + $('#modal .modal-body').empty(); + selected.forEach(function(row) { + const data = activeTable.fnGetData(row); + $('#modal .modal-body').append(cloneItem(data)); + }); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + $('#submit').click(function() { + cloneFromModal(activeTable); + }); + } + }); + + $('#reload').click(function() { + releasedFormTable.fnReloadAjax(); + archivedReleasedFormTable.fnReloadAjax(); + }); + // binding events + selectEvent(); + filterEvent(); +}); diff --git a/public/javascripts/review.js b/public/javascripts/review.js new file mode 100644 index 00000000..76c83e61 --- /dev/null +++ b/public/javascripts/review.js @@ -0,0 +1,221 @@ +/* global prefix: false, ajax401: false, updateAjaxURL: false, disableAjaxCache: false, travelerGlobal: false, Holder, moment */ +/* global selectColumn: false, reviewerIdColumn, fnGetSelected: false, selectEvent: false, filterEvent: false, sDomNoTools: false, reviewResultColumn, requestedOnColumn */ + +const path = window.location.pathname; + +function transformReview(review) { + const reviews = []; + const { reviewRequests = [], reviewResults = [] } = review; + reviewRequests.forEach(reviewRequest => { + const transformed = { + _id: reviewRequest._id, + requestedOn: reviewRequest.requestedOn, + requestedBy: reviewRequest.requestedBy, + result: reviewResults.reverse().find(reviewResult => { + return reviewResult.reviewerId === reviewRequest._id; + }), + }; + reviews.push(transformed); + }); + return reviews; +} + +function initTable(oTable) { + $.ajax({ + url: `${path}json`, + type: 'GET', + dataType: 'json', + }) + .done(function(json) { + oTable.fnClearTable(); + const reviews = transformReview(json); + oTable.fnAddData(reviews); + oTable.fnDraw(); + }) + .fail(function(jqXHR) { + if (jqXHR.status !== 401) { + $('#message').append( + '
Cannot reach the server for review information.
' + ); + } + }); +} + +function removeFromModal(cb) { + const ids = []; + $('#modal .modal-body .target').each(function() { + ids.push(this.id); + }); + $.ajax({ + url: `${path}requests/${ids.join()}`, + type: 'DELETE', + dataType: 'json', + }) + .done(function(json) { + json.forEach(function(id) { + const item = $(`#${id}`); + item.wrap(''); + item.addClass('text-success'); + }); + }) + .fail(function(jqXHR) { + $('.modal-body').append(`Error : ${jqXHR.responseText}`); + }) + .always(function() { + cb(); + }); +} + +function remove(oTable) { + const selected = fnGetSelected(oTable, 'row-selected'); + if (selected.length) { + $('#modalLabel').html( + `Remove the following ${selected.length} review requests? ` + ); + $('#modal .modal-body').empty(); + selected.forEach(function(row) { + const data = oTable.fnGetData(row); + $('#modal .modal-body').append( + `
${data._id}
` + ); + }); + $('#modal .modal-footer').html( + '' + ); + $('#remove').click(function(e) { + e.preventDefault(); + $('#remove').prop('disabled', true); + removeFromModal(function() { + initTable(oTable); + }); + }); + $('#modal').modal('show'); + } else { + $('#modalLabel').html('Alert'); + $('#modal .modal-body').html('No item has been selected!'); + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + } +} + +function inArray(name, ao) { + let i; + for (i = 0; i < ao.length; i += 1) { + if ((ao[i].username || ao[i]._id) === name) { + return true; + } + } + return false; +} + +function addTo(data, table, list) { + if (!data.name) { + $('#message').append( + `
${list} name is empty.
` + ); + return; + } + if (inArray(data.name, table.fnGetData())) { + const { name } = data; + // show message + $('#message').append( + `
${name} is already in the review list.
` + ); + return; + } + $.ajax({ + url: `${path}requests`, + type: 'POST', + contentType: 'application/json', + data: JSON.stringify(data), + processData: false, + success(res, status, jqXHR) { + $('#message').append( + `
${jqXHR.responseText}
` + ); + initTable(table); + }, + error(jqXHR) { + if (jqXHR.status !== 401) { + $('#message').append( + `
Cannot update the review list : ${jqXHR.responseText}
` + ); + } + }, + }); +} + +$(function() { + ajax401(prefix); + updateAjaxURL(prefix); + disableAjaxCache(); + + $('span.time').each(function() { + if ($(this).text()) { + $(this).text( + moment($(this).text()).format('dddd, MMMM Do YYYY, h:mm:ss a') + ); + } + }); + + if ($('#username').length) { + travelerGlobal.usernames.initialize(); + } + + $('#username').typeahead( + { + minLength: 1, + highlight: true, + hint: true, + }, + { + name: 'usernames', + display: 'displayName', + limit: 20, + source: travelerGlobal.usernames, + } + ); + + $('#username').on('typeahead:select', function() { + $('#add').attr('disabled', false); + }); + + const reviewAoColumns = [ + selectColumn, + reviewerIdColumn, + requestedOnColumn, + reviewResultColumn, + ]; + const reviewTable = $('#review-table').dataTable({ + aaData: [], + aoColumns: reviewAoColumns, + fnDrawCallback() { + Holder.run({ + images: 'img.user', + }); + }, + aaSorting: [[2, 'desc']], + sDom: sDomNoTools, + }); + + selectEvent(); + filterEvent(); + + $('#add').click(function(e) { + e.preventDefault(); + const data = {}; + data.name = $('#username').val(); + addTo(data, reviewTable, 'users'); + $('form[name="user"]')[0].reset(); + }); + + $('#review-remove').click(function() { + remove(reviewTable); + }); + + if ($('#username').length) { + initTable(reviewTable); + } +}); diff --git a/public/javascripts/reviews.js b/public/javascripts/reviews.js new file mode 100644 index 00000000..1c282245 --- /dev/null +++ b/public/javascripts/reviews.js @@ -0,0 +1,46 @@ +/* + global prefix, ajax401, updateAjaxURL, disableAjaxCache: false formLinkColumn, + titleColumn, fnAddFilterFoot, sDomNoTools, filterEvent, formStatusColumn, tagsColumn, Holder, + versionColumn, reviewRequestedByColumn, reviewRequestedOnColumn + */ + +$(function() { + ajax401(prefix); + updateAjaxURL(prefix); + disableAjaxCache(); + const reviewFormsAoColumns = [ + formLinkColumn, + titleColumn, + versionColumn, + formStatusColumn, + tagsColumn, + reviewRequestedByColumn, + reviewRequestedOnColumn, + ]; + fnAddFilterFoot('#form-table', reviewFormsAoColumns); + $('#form-table').dataTable({ + sAjaxSource: '/reviews/forms/json', + sAjaxDataProp: '', + fnDrawCallback() { + Holder.run({ + images: 'img.user', + }); + }, + bAutoWidth: false, + iDisplayLength: 10, + aLengthMenu: [ + [10, 50, 100, -1], + [10, 50, 100, 'All'], + ], + oLanguage: { + sLoadingRecords: 'Please wait - loading data from the server ...', + }, + bDeferRender: true, + aoColumns: reviewFormsAoColumns, + aaSorting: [[5, 'desc']], + sDom: sDomNoTools, + }); + + // binding events + filterEvent(); +}); diff --git a/public/javascripts/share.js b/public/javascripts/share.js index cdc94d1e..14b9c736 100644 --- a/public/javascripts/share.js +++ b/public/javascripts/share.js @@ -1,12 +1,11 @@ -/*global clearInterval: false, clearTimeout: false, document: false, event: false, frames: false, history: false, Image: false, location: false, name: false, navigator: false, Option: false, parent: false, screen: false, setInterval: false, setTimeout: false, window: false, XMLHttpRequest: false, FormData: false */ -/*global prefix: false, ajax401: false, updateAjaxURL: false, disableAjaxCache: false, access: false, travelerGlobal: false*/ -/*global selectColumn: false, useridColumn: false, userNameNoLinkColumn: false, groupIdColumn: false, accessColumn: false, fnGetSelected: false, selectEvent: false, filterEvent: false, sDomNoTools: false*/ +/* global prefix: false, ajax401: false, updateAjaxURL: false, disableAjaxCache: false, access: false, travelerGlobal: false, Holder */ +/* global selectColumn: false, useridColumn: false, userNameNoLinkColumn: false, groupIdColumn: false, accessColumn: false, fnGetSelected: false, selectEvent: false, filterEvent: false, sDomNoTools: false, groupNameColumn */ -var path = window.location.pathname; +const path = window.location.pathname; function initTable(list, oTable) { $.ajax({ - url: path + list + '/json', + url: `${path + list}/json`, type: 'GET', dataType: 'json', }) @@ -25,22 +24,22 @@ function initTable(list, oTable) { } function removeFromModal(list, cb) { - var ids = []; + const ids = []; $('#modal .modal-body .target').each(function() { ids.push(this.id); }); $.ajax({ - url: path + list + '/' + ids.join(), + url: `${path + list}/${ids.join()}`, type: 'DELETE', dataType: 'json', }) .done(function(json) { json.forEach(function(id) { - var item; + let item; if (list === 'users') { - item = $('#' + id); + item = $(`#${id}`); } else if (list === 'groups') { - item = $('[title="' + encodeURIComponent(id) + '"]'); + item = $(`[title="${encodeURIComponent(id)}"]`); } else { return; } @@ -49,7 +48,7 @@ function removeFromModal(list, cb) { }); }) .fail(function(jqXHR) { - $('.modal-body').append('Error : ' + jqXHR.responseText); + $('.modal-body').append(`Error : ${jqXHR.responseText}`); }) .always(function() { cb(); @@ -57,32 +56,22 @@ function removeFromModal(list, cb) { } function remove(list, oTable) { - var selected = fnGetSelected(oTable, 'row-selected'); + const selected = fnGetSelected(oTable, 'row-selected'); if (selected.length) { - $('#modalLabel').html( - 'Remove the following ' + selected.length + ' ' + list + '? ' - ); + $('#modalLabel').html(`Remove the following ${selected.length} ${list}? `); $('#modal .modal-body').empty(); selected.forEach(function(row) { - var data = oTable.fnGetData(row); + const data = oTable.fnGetData(row); if (list === 'users') { $('#modal .modal-body').append( - '
' + - data.username + - '
' + `
${data.username}
` ); } if (list === 'groups') { $('#modal .modal-body').append( - '
' + - data.groupname + - '
' + `
${data.groupname}
` ); } }); @@ -108,11 +97,11 @@ function remove(list, oTable) { } function modifyFromModal(list, cb) { - var number = $('#modal .modal-body div').length; + let number = $('#modal .modal-body div').length; $('#modal .modal-body div').each(function() { - var that = this; + const that = this; $.ajax({ - url: path + list + '/' + that.id, + url: `${path + list}/${that.id}`, type: 'PUT', contentType: 'application/json', processData: false, @@ -125,7 +114,7 @@ function modifyFromModal(list, cb) { $(that).addClass('text-success'); }) .fail(function(jqXHR) { - $(that).append(' : ' + jqXHR.responseText); + $(that).append(` : ${jqXHR.responseText}`); $(that).addClass('text-error'); }) .always(function() { @@ -140,30 +129,26 @@ function modifyFromModal(list, cb) { } function modify(list, oTable) { - var selected = fnGetSelected(oTable, 'row-selected'); + const selected = fnGetSelected(oTable, 'row-selected'); if (selected.length) { $('#modalLabel').html( - 'Modify the following ' + selected.length + ' ' + list + "' privilege? " + `Modify the following ${selected.length} ${list}' privilege? ` ); $('#modal .modal-body').empty(); $('#modal .modal-body').append( '
write
' ); selected.forEach(function(row) { - var data = oTable.fnGetData(row); + const data = oTable.fnGetData(row); if (list === 'users') { $('#modal .modal-body').append( - '
' + data.username + '
' + `
${data.username}
` ); } if (list === 'groups') { $('#modal .modal-body').append( - '
' + - data.groupname + - '
' + `
${data.groupname}
` ); } }); @@ -197,7 +182,7 @@ function modify(list, oTable) { } function inArray(name, ao) { - var i; + let i; for (i = 0; i < ao.length; i += 1) { if ((ao[i].username || ao[i]._id) === name) { return true; @@ -209,41 +194,31 @@ function inArray(name, ao) { function addto(data, table, list) { if (!!data.name || !!data.id) { if (inArray(data.name || data.id, table.fnGetData())) { - var name = data.name; + let { name } = data; if (list === 'groups') { name = data.id; } - //show message + // show message $('#message').append( - '
' + - name + - ' is already in the ' + - list + - ' share list.
' + `
${name} is already in the ${list} share list.
` ); } else { $.ajax({ - url: path + list + '/', + url: `${path + list}/`, type: 'POST', contentType: 'application/json', data: JSON.stringify(data), processData: false, - success: function(res, status, jqXHR) { + success(res, status, jqXHR) { $('#message').append( - '
' + - jqXHR.responseText + - '
' + `
${jqXHR.responseText}
` ); initTable(list, table); }, - error: function(jqXHR) { + error(jqXHR) { if (jqXHR.status !== 401) { $('#message').append( - '
Cannot update the ' + - list + - ' share list : ' + - jqXHR.responseText + - '
' + `
Cannot update the ${list} share list : ${jqXHR.responseText}
` ); } }, @@ -251,9 +226,7 @@ function addto(data, table, list) { } } else { $('#message').append( - '
' + - list + - ' name is empty.
' + `
${list} name is empty.
` ); } } @@ -266,9 +239,9 @@ $(function() { if (typeof access !== 'undefined') { $('select[name="public"]').val(access); } - var initAccess = $('select[name="public"]').val(); + let initAccess = $('select[name="public"]').val(); - $('select[name="public"]').click(function() { + $('select[name="public"]').change(function() { if ($('select[name="public"]').val() !== initAccess) { $('#update').attr('disabled', false); } else { @@ -278,35 +251,31 @@ $(function() { $('#update').click(function(e) { e.preventDefault(); - var value = $('select[name="public"]').val(); + const value = $('select[name="public"]').val(); if (initAccess === value) { $('#message').append( '
The setting is not changed.
' ); } else { $.ajax({ - url: path + 'public', + url: `${path}public`, type: 'PUT', contentType: 'application/json', data: JSON.stringify({ access: value, }), processData: false, - success: function(res, status, jqXHR) { + success(res, status, jqXHR) { $('#message').append( - '
' + - jqXHR.responseText + - '
' + `
${jqXHR.responseText}
` ); initAccess = value; $('#update').attr('disabled', true); }, - error: function(jqXHR) { + error(jqXHR) { if (jqXHR.status !== 401) { $('#message').append( - '
Cannot update the public access setting : ' + - jqXHR.responseText + - '
' + `
Cannot update the public access setting : ${jqXHR.responseText}
` ); } }, @@ -358,17 +327,17 @@ $(function() { $('#addgroup').attr('disabled', false); }); - var shareAoColumns = [ + const shareAoColumns = [ selectColumn, useridColumn, userNameNoLinkColumn, accessColumn, ]; - var shareTable = $('#share-table').dataTable({ + const shareTable = $('#share-table').dataTable({ aaData: [], // 'bAutoWidth': false, aoColumns: shareAoColumns, - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -377,12 +346,17 @@ $(function() { sDom: sDomNoTools, }); - var groupShareAoColumns = [selectColumn, groupIdColumn, groupNameColumn, accessColumn]; - var groupShareTable = $('#groupshare-table').dataTable({ + const groupShareAoColumns = [ + selectColumn, + groupIdColumn, + groupNameColumn, + accessColumn, + ]; + const groupShareTable = $('#groupshare-table').dataTable({ aaData: [], // 'bAutoWidth': false, aoColumns: groupShareAoColumns, - fnDrawCallback: function() { + fnDrawCallback() { Holder.run({ images: 'img.user', }); @@ -396,7 +370,7 @@ $(function() { $('#add').click(function(e) { e.preventDefault(); - var data = {}; + const data = {}; data.name = $('#username').val(); data.access = $('#access').prop('checked') ? 'write' : 'read'; addto(data, shareTable, 'users'); @@ -406,7 +380,7 @@ $(function() { $('#addgroup').click(function(e) { e.preventDefault(); - var data = {}; + const data = {}; data.id = $('#groupname') .val() .toLowerCase(); diff --git a/public/javascripts/table.js b/public/javascripts/table.js index 57cae0b7..213a6cd2 100644 --- a/public/javascripts/table.js +++ b/public/javascripts/table.js @@ -1,5 +1,5 @@ -/*global moment: false*/ -/*global prefix: false, linkTarget: false*/ +/* global moment: false */ +/* global prefix: false, linkTarget: false, userid */ function formatDate(date) { return date ? moment(date).fromNow() : ''; @@ -25,7 +25,7 @@ function selectEvent() { function selectMultiEvent(oTable) { $(oTable.$('tbody')).on('click', 'input.select-row', function(e) { - var tr = $(e.target).closest('tr'); + const tr = $(e.target).closest('tr'); if ($(tr).hasClass('row-selected')) { $(tr).removeClass('row-selected'); } else { @@ -36,7 +36,7 @@ function selectMultiEvent(oTable) { function selectOneEvent(oTable) { $('tbody').on('click', 'input.select-row', function(e) { - var tr = $(e.target).closest('tr'); + const tr = $(e.target).closest('tr'); if ($(tr).hasClass('row-selected')) { $(tr).removeClass('row-selected'); } else { @@ -52,20 +52,16 @@ function selectOneEvent(oTable) { function filterEvent() { $('.filter').on('keyup', 'input', function(e) { - var table = $(this).closest('table'); - var th = $(this).closest('th'); - var filter = $(this).closest('.filter'); - var index; + const table = $(this).closest('table'); + const th = $(this).closest('th'); + const filter = $(this).closest('.filter'); + let index; if (filter.is('thead')) { index = $('thead.filter th', table).index(th); - $('tfoot.filter th:nth-child(' + (index + 1) + ') input', table).val( - this.value - ); + $(`tfoot.filter th:nth-child(${index + 1}) input`, table).val(this.value); } else { index = $('tfoot.filter th', table).index(th); - $('thead.filter th:nth-child(' + (index + 1) + ') input', table).val( - this.value - ); + $(`thead.filter th:nth-child(${index + 1}) input`, table).val(this.value); } table.dataTable().fnFilter(this.value, index); }); @@ -74,7 +70,7 @@ function filterEvent() { function dateColumn(title, key) { return { sTitle: title, - mData: function(source, type, val) { + mData(source, type, val) { if (type === 'sort') { return source[key]; } @@ -87,7 +83,7 @@ function dateColumn(title, key) { function longDateColumn(title, key) { return { sTitle: title, - mData: function(source, type, val) { + mData(source, type, val) { if (type === 'sort') { return source[key]; } @@ -103,24 +99,16 @@ function personColumn(title, key) { sTitle: title, mData: key, sDefaultContent: '', - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data; - } else if (data) { - return ( - '' - ); - } else { - return ''; } + if (data) { + return ``; + } + return ''; }, bFilter: true, }; @@ -129,7 +117,7 @@ function personColumn(title, key) { function keyValueColumn(collection, key) { return { sTitle: key, - mData: collection + '.' + key + '.value', + mData: `${collection}.${key}.value`, sDefaultContent: '', bFilter: true, }; @@ -138,18 +126,18 @@ function keyValueColumn(collection, key) { function keyLabelColumn(key) { return { sTitle: 'label', - mData: 'user_defined.' + key + '.label', + mData: `user_defined.${key}.label`, sDefaultContent: '', bFilter: true, }; } function valueLabel(data) { - var output = ''; + let output = ''; if (data.value) { - output += '' + data.value + ''; + output += `${data.value}`; if (data.label) { - output += ' (' + data.label + ')'; + output += ` (${data.label})`; } } return output; @@ -158,16 +146,16 @@ function valueLabel(data) { function keyValueLableColumn(key) { return { sTitle: key, - mData: 'user_defined.' + key, + mData: `user_defined.${key}`, sDefaultContent: '', - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data.value; - } else if (data) { + } + if (data) { return valueLabel(data); - } else { - return ''; } + return ''; }, bFilter: true, }; @@ -178,16 +166,8 @@ function personNameColumn(title, key) { sTitle: title, mData: key, sDefaultContent: '', - mRender: function(data, type, full) { - return ( - '' + - data + - '' - ); + mRender(data, type, full) { + return `${data}`; }, bFilter: true, }; @@ -208,11 +188,11 @@ function fnUnwrap(oTableLocal) { } function fnGetSelected(oTableLocal, selectedClass) { - var aReturn = [], - i; - var aTrs = oTableLocal.fnGetNodes(); + const aReturn = []; + let i; + const aTrs = oTableLocal.fnGetNodes(); - for (i = 0; i < aTrs.length; i++) { + for (i = 0; i < aTrs.length; i += 1) { if ($(aTrs[i]).hasClass(selectedClass)) { aReturn.push(aTrs[i]); } @@ -222,22 +202,22 @@ function fnGetSelected(oTableLocal, selectedClass) { function fnGetSelectedInPage(oTableLocal, selectedClass, current) { if (current) { - return oTableLocal.$('tr.' + selectedClass, { + return oTableLocal.$(`tr.${selectedClass}`, { page: 'current', }); } - return oTableLocal.$('tr.' + selectedClass); + return oTableLocal.$(`tr.${selectedClass}`); } function fnDeselect(oTableLocal, selectedClass, checkboxClass) { - var aTrs = oTableLocal.fnGetNodes(); - var i; + const aTrs = oTableLocal.fnGetNodes(); + let i; - for (i = 0; i < aTrs.length; i++) { + for (i = 0; i < aTrs.length; i += 1) { if ($(aTrs[i]).hasClass(selectedClass)) { $(aTrs[i]).removeClass(selectedClass); $(aTrs[i]) - .find('input.' + checkboxClass + ':checked') + .find(`input.${checkboxClass}:checked`) .prop('checked', false); } } @@ -245,8 +225,8 @@ function fnDeselect(oTableLocal, selectedClass, checkboxClass) { function fnSelectAll(oTableLocal, selectedClass, checkboxClass, current) { fnDeselect(oTableLocal, selectedClass, checkboxClass); - var rows; - var i; + let rows; + let i; if (current) { rows = oTableLocal.$('tr', { page: 'current', @@ -260,7 +240,7 @@ function fnSelectAll(oTableLocal, selectedClass, checkboxClass, current) { for (i = 0; i < rows.length; i += 1) { $(rows[i]).addClass(selectedClass); $(rows[i]) - .find('input.' + checkboxClass) + .find(`input.${checkboxClass}`) .prop('checked', true); } } @@ -269,7 +249,7 @@ function fnSetDeselect(nTr, selectedClass, checkboxClass) { if ($(nTr).hasClass(selectedClass)) { $(nTr).removeClass(selectedClass); $(nTr) - .find('input.' + checkboxClass + ':checked') + .find(`input.${checkboxClass}:checked`) .prop('checked', false); } } @@ -281,13 +261,11 @@ function fnSetColumnsVis(oTableLocal, columns, show) { } function fnAddFilterFoot(sTable, aoColumns) { - var tr = $(''); + const tr = $(''); aoColumns.forEach(function(c) { if (c.bFilter) { tr.append( - '' + `` ); } else { tr.append(''); @@ -297,13 +275,11 @@ function fnAddFilterFoot(sTable, aoColumns) { } function fnAddFilterHead(sTable, aoColumns) { - var tr = $(''); + const tr = $(''); aoColumns.forEach(function(c) { if (c.bFilter) { tr.append( - '' + `` ); } else { tr.append(''); @@ -313,7 +289,7 @@ function fnAddFilterHead(sTable, aoColumns) { } function formatTravelerStatus(s) { - var status = { + const status = { '1': 'active', '1.5': 'submitted for completion', '2': 'completed', @@ -327,23 +303,10 @@ function formatTravelerStatus(s) { return 'unknown'; } -function formatFormStatus(s) { - var status = { - '0': 'editable', - '0.5': 'ready to publish', - '1': 'published', - '2': 'obsoleted', - }; - if (status[s.toString()]) { - return status[s.toString()]; - } - return 'unknown'; -} - $.fn.dataTableExt.oApi.fnAddDataAndDisplay = function(oSettings, aData) { /* Add the data */ - var iAdded = this.oApi._fnAddData(oSettings, aData); - var nAdded = oSettings.aoData[iAdded].nTr; + const iAdded = this.oApi._fnAddData(oSettings, aData); + const nAdded = oSettings.aoData[iAdded].nTr; /* Need to re-filter and re-sort the table to get positioning correct, not perfect * as this will actually redraw the table on screen, but the update should be so fast (and @@ -352,10 +315,11 @@ $.fn.dataTableExt.oApi.fnAddDataAndDisplay = function(oSettings, aData) { this.oApi._fnReDraw(oSettings); /* Find it's position in the table */ - var iPos = -1; - var i, iLen; + let iPos = -1; + let i; + let iLen; - for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) { + for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i += 1) { if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nAdded) { iPos = i; break; @@ -383,9 +347,10 @@ $.fn.dataTableExt.oApi.fnDisplayRow = function(oSettings, nRow) { } // Find the node in the table - var iPos = -1; - var i, iLen; - for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) { + let iPos = -1; + let i; + let iLen; + for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i += 1) { if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nRow) { iPos = i; break; @@ -402,7 +367,7 @@ $.fn.dataTableExt.oApi.fnDisplayRow = function(oSettings, nRow) { this.oApi._fnDraw(oSettings); }; -var selectColumn = { +const selectColumn = { sTitle: '', sDefaultContent: '', @@ -410,89 +375,65 @@ var selectColumn = { asSorting: ['desc', 'asc'], }; -var userIdColumn = { +const userIdColumn = { sTitle: 'User Id', - mRender: function(data) { - return ''; + mRender(data) { + return ``; }, mData: '_id', bSortable: true, sWidth: '30px', }; -var previewColumn = { +const previewColumn = { sTitle: '', mData: '_id', bSortable: false, - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, sWidth: '25px', }; -var removeColumn = { +const removeColumn = { sTitle: '', mData: '_id', bSortable: false, - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, }; -var referenceFormLinkColumn = { +const referenceFormLinkColumn = { sTitle: 'Ref', mData: 'reference', - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, bSortable: false, sWidth: '45px', }; -var formColumn = { +const formColumn = { sTitle: 'Link', mData: '_id', - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, bSortable: false, sWidth: '45px', }; -var aliasColumn = { +const aliasColumn = { sTitle: 'Alias', mData: 'alias', bFilter: true, }; -var activatedOnColumn = { +const activatedOnColumn = { sTitle: 'Activated', - mData: function(source, type) { - var a = source.activatedOn; + mData(source, type) { + const a = source.activatedOn; if (type === 'sort') { return a[a.length - 1]; } @@ -501,7 +442,7 @@ var activatedOnColumn = { sDefaultContent: '', }; -var idColumn = { +const idColumn = { sTitle: '', mData: '_id', bVisible: false, @@ -510,16 +451,8 @@ var idColumn = { const formLinkColumn = { sTitle: '', mData: '_id', - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, bSortable: false, }; @@ -527,58 +460,42 @@ const formLinkColumn = { const releasedFormLinkColumn = { sTitle: '', mData: '_id', - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, bSortable: false, }; -var formConfigLinkColumn = { +const formConfigLinkColumn = { sTitle: '', mData: '_id', - mRender: function(data, type, full) { - return ( - '' - ); + mRender(data, type, full) { + return ``; }, bSortable: false, }; function cloneForm(id, type, title) { $.ajax({ - url: '/' + type + '/' + id + '/clone', + url: `/${type}/${id}/clone`, type: 'POST', contentType: 'application/json', data: JSON.stringify({ - title: title, + title, }), }) .done(function(d) { $('#message').append( - '
' + - 'The form was cloned. ' + - d + - '
' + `${'
' + + 'The form was cloned. '}${d}
` ); }) .fail(function(jqXHR) { $('#message').append( - '
' + - '' + - jqXHR.responseText + - '.
' + `${'
' + + ''}${ + jqXHR.responseText + }.
` ); }); } @@ -604,149 +521,240 @@ function cloneColumn(type) { return { sTitle: 'Clone', mData: '_id', - mRender: function(data) { - return ( - '' - ); + mRender(data) { + return ``; }, bSortable: false, }; } -var formCloneColumn = cloneColumn('forms'); +const formCloneColumn = cloneColumn('forms'); -var releasedFormCloneColumn = cloneColumn('released-forms'); +const releasedFormCloneColumn = cloneColumn('released-forms'); -var formShareLinkColumn = { +const formShareLinkColumn = { sTitle: '', - mData: function(source) { + mData(source) { if (source.publicAccess >= 0) { - return ( - '' - ); + return ``; } - return ( - '' - ); + return ``; + }, + bSortable: false, +}; + +const formReviewLinkColumn = { + sTitle: '', + mData(source) { + return ``; }, bSortable: false, }; -var createdOnColumn = dateColumn('Created', 'createdOn'); -var createdByColumn = personColumn('Created by', 'createdBy'); -var ownerColumn = { +const reviewerIdColumn = personColumn('Reviewer', '_id'); + +const requestedOnColumn = dateColumn('Requested on', 'requestedOn'); + +const reviewRequestedOnColumn = { + sTitle: 'Requested on', + mData(source, type) { + if (!source.__review) { + return ''; + } + const requests = source.__review.reviewRequests; + if (!requests) { + return ''; + } + const request = requests.find(function(r) { + return r._id === userid; + }); + if (!request) { + return ''; + } + return formatDate(request.requestedOn); + }, + bFilter: true, +}; + +const reviewRequestedByColumn = { + sTitle: 'Requested by', + mData(source, type) { + if (!source.__review) { + return ''; + } + const requests = source.__review.reviewRequests; + if (!requests) { + return ''; + } + const request = requests.find(function(r) { + return r._id === userid; + }); + if (!request) { + return ''; + } + return ``; + }, + bFilter: true, +}; + +const reviewersColumn = { + sTitle: 'Reviewers', + mData(source, type) { + if (source.__review) { + const requests = source.__review.reviewRequests; + if (!requests) { + return ''; + } + if (requests.length === 0) { + return ''; + } + const reviews = requests.map(function(r) { + if (type === 'filter' || type === 'sort') { + return r._id; + } + return ``; + }); + if (type === 'filter' || type === 'sort') { + return reviews.join('; '); + } + return reviews.join(' '); + } + return ''; + }, + bFilter: true, +}; + +const firstReviewRequestedOnColumn = { + sTitle: 'Requested on', + mData(source, type) { + if (source.__review) { + const requests = source.__review.reviewRequests; + if (!requests) { + return ''; + } + if (requests.length === 0) { + return ''; + } + const first = requests[0]; + if (type === 'sort') { + return first.requestedOn; + } + return formatDate(first.requestedOn); + } + return ''; + }, +}; + +const createdOnColumn = dateColumn('Created', 'createdOn'); +const createdByColumn = personColumn('Created by', 'createdBy'); +const ownerColumn = { sTitle: 'Owner', sDefaultContent: '', - mData: function(source, type) { - var owner = source.owner || source.createdBy; + mData(source, type) { + const owner = source.owner || source.createdBy; if (type === 'sort' || type === 'filter') { return owner; - } else if (owner) { - return ( - '' - ); - } else { - return ''; } + if (owner) { + return ``; + } + return ''; }, bFilter: true, }; -var clonedByColumn = personColumn('Cloned by', 'clonedBy'); +const clonedByColumn = personColumn('Cloned by', 'clonedBy'); -var updatedOnColumn = dateColumn('Updated', 'updatedOn'); -var updatedByColumn = personColumn('Updated by', 'updatedBy'); +const updatedOnColumn = dateColumn('Updated', 'updatedOn'); +const updatedByColumn = personColumn('Updated by', 'updatedBy'); const releasedOnColumn = longDateColumn('Released', 'releasedOn'); const releasedByColumn = personColumn('Released by', 'releasedBy'); -var transferredOnColumn = dateColumn('transferred', 'transferredOn'); +const transferredOnColumn = dateColumn('transferred', 'transferredOn'); const archivedOnColumn = dateColumn('Archived', 'archivedOn'); const archivedByColumn = personColumn('Archived by', 'archivedBy'); -var deadlineColumn = dateColumn('Deadline', 'deadline'); +const deadlineColumn = dateColumn('Deadline', 'deadline'); -var tagsColumn = { +const tagsColumn = { sTitle: 'Tags', sDefaultContent: '', - mData: function(source, type, val) { + mData(source, type, val) { return source.tags || []; }, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data.join(' '); - } else { - return data.join('; '); } + return data.join('; '); }, bFilter: true, }; -var keysColumn = { +const keysColumn = { sTitle: 'Reporting IDs', sDefaultContent: '', - mData: function(source, type, val) { + mData(source, type, val) { if (source.mapping) { return Object.keys(source.mapping); } return []; }, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data.join(' '); - } else { - return data.join('; '); } + return data.join('; '); }, bAutoWidth: false, sWidth: '210px', bFilter: true, }; -var commentsColumn = { - sTitle: 'Comments', +const reviewResultColumn = { + sTitle: 'Latest review result', sDefaultContent: '', - mData: 'comments', + mData: 'result', + mRender(data) { + if (!data) { + return 'not reported'; + } + if (data.result === '1') { + return 'approved'; + } + if (data.result === '2') { + return 'comment'; + } + return ''; + }, + bFilter: true, +}; + +const commentColumn = { + sTitle: 'Comment', + sDefaultContent: '', + mData: 'comment', bFilter: true, }; -var titleColumn = { +const titleColumn = { sTitle: 'Title', sDefaultContent: '', mData: 'title', bFilter: true, }; -var versionColumn = { +const versionColumn = { sTitle: 'Ver', mData: '_v', sDefaultContent: '', @@ -754,7 +762,7 @@ var versionColumn = { sWidth: '45px', }; -var releasedFormVersionColumn = { +const releasedFormVersionColumn = { sTitle: 'Ver', mData: 'ver', sDefaultContent: '', @@ -762,7 +770,7 @@ var releasedFormVersionColumn = { sWidth: '45px', }; -var formTypeColumn = { +const formTypeColumn = { sTitle: 'Type', mData: 'formType', sDefaultContent: 'normal', @@ -770,10 +778,10 @@ var formTypeColumn = { }; function formatFormStatus(s) { - var status = { + const status = { '0': 'draft', - '0.5': 'submitted', - '1': 'pre released', + '0.5': 'under review', + '1': 'closed', '2': 'archived', }; if (status[s.toString()]) { @@ -782,16 +790,16 @@ function formatFormStatus(s) { return 'unknown'; } -var formStatusColumn = { +const formStatusColumn = { sTitle: 'Status', - mData: function(source, type, val) { + mData(source, type, val) { return formatFormStatus(source.status); }, bFilter: true, }; function formatReleasedFormStatus(s) { - var status = { + const status = { '1': 'released', '2': 'archived', }; @@ -801,180 +809,96 @@ function formatReleasedFormStatus(s) { return 'unknown'; } -var releasedFormStatusColumn = { +const releasedFormStatusColumn = { sTitle: 'Status', - mData: function(source, type, val) { + mData(source, type, val) { return formatReleasedFormStatus(source.status); }, bFilter: true, }; -var travelerLinkColumn = { +const travelerLinkColumn = { sTitle: '', - mData: function(source, type, val) { + mData(source, type, val) { if (source.hasOwnProperty('url')) { - return ( - '' - ); + return ``; } if (source.hasOwnProperty('_id')) { - return ( - '' - ); + return ``; } return 'unknown'; }, bSortable: false, }; -var travelerConfigLinkColumn = { +const travelerConfigLinkColumn = { sTitle: '', mData: '_id', - mRender: function(data, type, full) { - return ( - '' - ); + mRender(data, type, full) { + return ``; }, bSortable: false, }; -var travelerShareLinkColumn = { +const travelerShareLinkColumn = { sTitle: '', - mData: function(source) { + mData(source) { if (source.publicAccess >= 0) { - return ( - '' - ); + return ``; } - return ( - '' - ); + return ``; }, bSortable: false, }; -var binderLinkColumn = { +const binderLinkColumn = { sTitle: '', - mData: function(source, type, val) { + mData(source, type, val) { if (source.hasOwnProperty('url')) { - return ( - '' - ); + return ``; } if (source.hasOwnProperty('_id')) { - return ( - '' - ); + return ``; } return 'unknown'; }, bSortable: false, }; -var binderConfigLinkColumn = { +const binderConfigLinkColumn = { sTitle: '', mData: '_id', - mRender: function(data, type, full) { - return ( - '' - ); + mRender(data, type, full) { + return ``; }, bSortable: false, }; -var binderShareLinkColumn = { +const binderShareLinkColumn = { sTitle: '', - mData: function(source) { + mData(source) { if (source.publicAccess >= 0) { - return ( - '' - ); + return ``; } - return ( - '' - ); + return ``; }, bSortable: false, }; -var deviceTravelerLinkColumn = { +const deviceTravelerLinkColumn = { sTitle: '', mData: 'inventoryId', - mRender: function(data, type, full) { - return ( - '' - ); + mRender(data, type, full) { + return ``; }, bSortable: false, }; function progressBar(active, finished, inProgress, text, width) { - var w = width || '100px'; - var t = text || ''; - var bar = $( - '
' + - t + - '
' + const w = width || '100px'; + const t = text || ''; + const bar = $( + `
${t}
` ); if (active) { bar.addClass('active').addClass('progress-striped'); @@ -982,13 +906,13 @@ function progressBar(active, finished, inProgress, text, width) { return bar[0].outerHTML; } -var travelerProgressColumn = { +const travelerProgressColumn = { sTitle: 'Estimated progress', bSortable: true, sType: 'numeric', bAutoWidth: false, sWidth: '105px', - mData: function(source, type) { + mData(source, type) { if (source.status === 2) { if (type === 'sort') { return 1; @@ -996,8 +920,6 @@ var travelerProgressColumn = { return progressBar(false, 100, 0); } - var inProgress; - if (!source.hasOwnProperty('totalInput')) { if (type === 'sort') { return 0; @@ -1019,25 +941,27 @@ var travelerProgressColumn = { return 'unknown'; } - inProgress = Math.floor((source.finishedInput / source.totalInput) * 100); + const inProgress = Math.floor( + (source.finishedInput / source.totalInput) * 100 + ); return progressBar( source.status === 1, 0, inProgress, - '' + source.finishedInput + ' / ' + source.totalInput + `${source.finishedInput} / ${source.totalInput}` ); }, }; -var workProgressColumn = { +const workProgressColumn = { sTitle: 'Estimated progress', bSortable: true, sType: 'numeric', bAutoWidth: false, sWidth: '210px', - mData: function(source, type) { - var w = '200px'; + mData(source, type) { + const w = '200px'; if (source.status === 2) { if (type === 'sort') { return 1; @@ -1046,13 +970,13 @@ var workProgressColumn = { false, 100, 0, - '' + source.value + ' + 0 / ' + source.value, + `${source.value} + 0 / ${source.value}`, w ); } - var inProgress = source.inProgress; - var finished = 0; + const { inProgress } = source; + let finished = 0; if (source.hasOwnProperty('finished')) { finished = source.finished; @@ -1066,24 +990,21 @@ var workProgressColumn = { source.status === 1, finished * 100, inProgress * 100, - '' + - Math.round(finished * source.value) + - ' + ' + - Math.round(inProgress * source.value) + - ' / ' + - Math.round(source.value), + `${Math.round(finished * source.value)} + ${Math.round( + inProgress * source.value + )} / ${Math.round(source.value)}`, w ); }, }; -var binderWorkProgressColumn = { +const binderWorkProgressColumn = { sTitle: 'Work progress', bSortable: true, sType: 'numeric', bAutoWidth: false, sWidth: '105px', - mData: function(source, type, val) { + mData(source, type, val) { if (source.status === 2) { if (type === 'sort') { return 1; @@ -1098,8 +1019,8 @@ var binderWorkProgressColumn = { return progressBar(false, 0, 0); } - var inProgress = source.inProgressWork / source.totalWork; - var finished = source.finishedWork / source.totalWork; + const inProgress = source.inProgressWork / source.totalWork; + const finished = source.finishedWork / source.totalWork; if (type === 'sort') { return finished + inProgress; @@ -1109,25 +1030,18 @@ var binderWorkProgressColumn = { source.status === 1, finished * 100, inProgress * 100, - '' + - source.finishedWork + - ' / ' + - source.totalWork + - ' + ' + - source.finishedInput + - ' / ' + - source.totalInput + `${source.finishedWork} / ${source.totalWork} + ${source.finishedInput} / ${source.totalInput}` ); }, }; -var binderValueProgressColumn = { +const binderValueProgressColumn = { sTitle: 'Value progress', bSortable: true, sType: 'numeric', bAutoWidth: false, sWidth: '105px', - mData: function(source, type, val) { + mData(source, type, val) { if (source.status === 2) { if (type === 'sort') { return 1; @@ -1142,8 +1056,8 @@ var binderValueProgressColumn = { return progressBar(false, 0, 0); } - var inProgress = source.inProgressValue / source.totalValue; - var finished = source.finishedValue / source.totalValue; + const inProgress = source.inProgressValue / source.totalValue; + const finished = source.finishedValue / source.totalValue; if (type === 'sort') { return finished + inProgress; @@ -1153,27 +1067,23 @@ var binderValueProgressColumn = { source.status === 1, finished * 100, inProgress * 100, - '' + - Math.round(source.finishedValue) + - ' + ' + - Math.round(source.inProgressValue) + - ' / ' + - Math.round(source.totalValue) + `${Math.round(source.finishedValue)} + ${Math.round( + source.inProgressValue + )} / ${Math.round(source.totalValue)}` ); }, }; -var deviceColumn = { +const deviceColumn = { sTitle: 'Devices', - mData: function(source, type, val) { + mData(source, type, val) { return source.devices || []; }, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data.join(' '); - } else { - return data.join('; '); } + return data.join('; '); }, bFilter: true, }; @@ -1181,40 +1091,24 @@ var deviceColumn = { function usersColumn(title, prop) { return { sTitle: title, - mData: function(source, type) { + mData(source, type) { if (source[prop]) { if (source[prop].length === 0) { return ''; } - var names = source[prop].map(function(u) { + const names = source[prop].map(function(u) { if (!u._id) { return u; } if (type === 'filter' || type === 'sort') { return u.username; - } else { - return ( - '' - ); } + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); - } else { - return names.join(' '); } + return names.join(' '); } return ''; }, @@ -1225,49 +1119,33 @@ function usersColumn(title, prop) { function usersFilteredColumn(title, filter) { return { sTitle: title, - mData: function(source, type) { - var users = filter(source); + mData(source, type) { + const users = filter(source); if (users.length === 0) { return ''; } - var names = users.map(function(u) { + const names = users.map(function(u) { if (!u._id) { return u; } if (type === 'filter' || type === 'sort') { return u.username; - } else { - return ( - '' - ); } + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); - } else { - return names.join(' '); } + return names.join(' '); }, bFilter: true, }; } -var sharedWithColumn = usersColumn('Shared with', 'sharedWith'); +const sharedWithColumn = usersColumn('Shared with', 'sharedWith'); function notIn(user, users) { - var i; + let i; for (i = 0; i < users.length; i += 1) { if (users[i]._id === user._id) { return false; @@ -1276,8 +1154,8 @@ function notIn(user, users) { return true; } -var manPowerColumn = usersFilteredColumn('Powered by', function(source) { - var out = []; +const manPowerColumn = usersFilteredColumn('Powered by', function(source) { + const out = []; if (source.manPower) { source.manPower.forEach(function(m) { if (notIn(m, out)) { @@ -1298,16 +1176,16 @@ var manPowerColumn = usersFilteredColumn('Powered by', function(source) { return out; }); -var filledByColumn = usersColumn('Filled by', 'manPower'); +const filledByColumn = usersColumn('Filled by', 'manPower'); -var sharedGroupColumn = { +const sharedGroupColumn = { sTitle: 'Shared groups', - mData: function(source, type, val) { + mData(source, type, val) { if (source.sharedGroup) { if (source.sharedGroup.length === 0) { return ''; } - var names = source.sharedGroup.map(function(g) { + const names = source.sharedGroup.map(function(g) { return g.groupname; }); return names.join('; '); @@ -1317,90 +1195,79 @@ var sharedGroupColumn = { bFilter: true, }; -var statusColumn = { +const statusColumn = { sTitle: 'Status', mData: 'status', - mRender: function(data, type, full) { + mRender(data, type, full) { return formatTravelerStatus(data); }, bFilter: true, }; -var formStatusColumn = { - sTitle: 'Status', - mData: 'status', - mRender: function(data, type, full) { - return formatFormStatus(data); - }, - bFilter: true, -}; - -/*shared user columns*/ -var useridColumn = personColumn('User', '_id'); +/* shared user columns */ +const useridColumn = personColumn('User', '_id'); -var useridNoLinkColumn = { +const useridNoLinkColumn = { sTitle: 'User id', mData: '_id', sDefaultContent: '', bFilter: true, }; -var userNameColumn = { +const userNameColumn = { sTitle: 'Full name', mData: 'username', sDefaultContent: '', - mRender: function(data, type, full) { - return ( - '' + data + '' - ); + mRender(data, type, full) { + return `${data}`; }, bFilter: true, }; -var userNameNoLinkColumn = { +const userNameNoLinkColumn = { sTitle: 'Full name', mData: 'username', sDefaultContent: '', bFilter: true, }; -var fullNameNoLinkColumn = { +const fullNameNoLinkColumn = { sTitle: 'Full name', mData: 'name', sDefaultContent: '', bFilter: true, }; -var groupIdColumn = { +const groupIdColumn = { sTitle: 'Group id', mData: '_id', sDefaultContent: '', bFilter: true, - mRender: function(data) { - return '' + data + ''; + mRender(data) { + return `${data}`; }, }; -var displayNameColumn = { +const displayNameColumn = { sTitle: 'Display Name', mData: 'name', sDefaultContent: '', bFilter: true, }; -var groupNameColumn = { +const groupNameColumn = { sTitle: 'Group Name', mData: 'groupname', sDefaultContent: '', bFilter: true, }; -var membersColumn = { +const membersColumn = { sTitle: 'Member(s)', mData: 'members', sDefaultContent: '', bFilter: false, - mRender: function(data) { + mRender(data) { data.sort(function(a, b) { if (a.name < b.name) { return -1; @@ -1410,195 +1277,178 @@ var membersColumn = { } return 0; }); - var result = data.map(function(d) { - return '
  • ' + d.name + '
  • '; + const result = data.map(function(d) { + return `
  • ${d.name}
  • `; }); - return '
      ' + result.join('') + '
    '; + return `
      ${result.join('')}
    `; }, }; -var accessColumn = { +const accessColumn = { sTitle: 'Privilege', mData: 'access', sDefaultContent: '', - mRender: function(data, type, full) { + mRender(data, type, full) { if (data === 0) { return 'read'; } if (data === 1) { return 'write'; } + return 'unknown'; }, bFilter: true, }; -/*user columns*/ -var rolesColumn = { +/* user columns */ +const rolesColumn = { sTitle: 'Roles', mData: 'roles', sDefaultContent: '', - mRender: function(data, type, full) { + mRender(data, type, full) { return data.join(); }, bFilter: true, }; -var lastVisitedOnColumn = dateColumn('Last visited', 'lastVisitedOn'); +const lastVisitedOnColumn = dateColumn('Last visited', 'lastVisitedOn'); -/*device columns*/ +/* device columns */ -var serialColumn = { +const serialColumn = { sTitle: 'Serial', mData: 'inventoryId', sDefaultContent: '', bFilter: true, }; -var typeColumn = { +const typeColumn = { sTitle: 'Type', mData: 'deviceType.name', sDefaultContent: '', bFilter: true, }; -var descriptionColumn = { +const descriptionColumn = { sTitle: 'Description', mData: 'deviceType.description', sDefaultContent: '', bFilter: true, }; -var modifiedOnColumn = dateColumn('Modified', 'dateModified'); +const modifiedOnColumn = dateColumn('Modified', 'dateModified'); -var modifiedByColumn = { +const modifiedByColumn = { sTitle: 'Modified by', mData: 'modifiedBy', sDefaultContent: '', bFilter: true, }; -var addedByColumn = personColumn('Added by', 'addedBy'); +const addedByColumn = personColumn('Added by', 'addedBy'); -var addedOnColumn = dateColumn('Added on', 'addedOn'); +const addedOnColumn = dateColumn('Added on', 'addedOn'); -var sequenceColumn = { +const sequenceColumn = { sTitle: 'Sequence', mData: 'sequence', sClass: 'editable', sType: 'numeric', bFilter: true, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data; - } else { - return ( - '' - ); } + return ``; }, }; -var sColumn = { +const sColumn = { sTitle: 'S', mData: 'sequence', bFilter: true, }; -var priorityColumn = { +const priorityColumn = { sTitle: 'Priority', mData: 'priority', sClass: 'editable', sType: 'numeric', bFilter: true, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data; - } else { - return ( - '' - ); } + return ``; }, }; -var pColumn = { +const pColumn = { sTitle: 'P', mData: 'priority', bFilter: true, }; -var valueColumn = { +const valueColumn = { sTitle: 'Value', mData: 'value', sClass: 'editable', sType: 'numeric', bFilter: true, - mRender: function(data, type) { + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data; - } else { - return ( - '' - ); } + return ``; }, }; -var vColumn = { +const vColumn = { sTitle: 'V', mData: 'value', bFilter: true, }; -var colorColumn = { +const colorColumn = { sTitle: 'Color', mData: 'color', sClass: 'editable', - mRender: function(data, type) { - var snippet; + mRender(data, type) { if (type === 'sort' || type === 'filter') { return data; - } else { - snippet = $( - '' - ); - $('option[value="' + data + '"]', snippet).attr('selected', 'selected'); - return snippet[0].outerHTML; } + const snippet = $( + '' + ); + $(`option[value="${data}"]`, snippet).attr('selected', 'selected'); + return snippet[0].outerHTML; }, }; -var cColumn = { +const cColumn = { sTitle: 'C', mData: 'color', - mRender: function(data, type) { - var snippet = $(''); + mRender(data, type) { + const snippet = $(''); if (type === 'sort' || type === 'filter') { return data; - } else { - switch (data) { - case 'blue': - snippet.addClass('text-info'); - break; - case 'green': - snippet.addClass('text-success'); - break; - case 'yellow': - snippet.addClass('text-warning'); - break; - case 'red': - snippet.addClass('text-error'); - break; - default: - } - return snippet[0].outerHTML; } + switch (data) { + case 'blue': + snippet.addClass('text-info'); + break; + case 'green': + snippet.addClass('text-success'); + break; + case 'yellow': + snippet.addClass('text-warning'); + break; + case 'red': + snippet.addClass('text-error'); + break; + default: + } + return snippet[0].outerHTML; }, }; @@ -1614,9 +1464,9 @@ function exportVisible(tool) { }; } -var oTableTools = { +const oTableTools = { sSwfPath: prefix - ? prefix + '/datatables/swf/copy_csv_xls_pdf.swf' + ? `${prefix}/datatables/swf/copy_csv_xls_pdf.swf` : '/datatables/swf/copy_csv_xls_pdf.swf', aButtons: [ exportVisible('copy'), @@ -1633,17 +1483,17 @@ var oTableTools = { ], }; -var sDom = +const sDom = "<'row-fluid'<'span6'<'control-group'T>>><'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>"; -var sDom2i = +const sDom2i = "<'row-fluid'<'span6'<'control-group'T>>><'row-fluid'<'span3'l><'span3'i><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>"; -var sDom2i1p = +const sDom2i1p = "<'row-fluid'<'span6'<'control-group'T>>><'row-fluid'<'span3'l><'span3'i><'span3'r><'span3'f>>t<'row-fluid'<'span6'i><'span6'p>>"; -var sDomNoTools = +const sDomNoTools = "<'row-fluid'<'span4'l><'span4'<'text-center'r>><'span4'f>>t<'row-fluid'<'span6'i><'span6'p>>"; -var sDomNoTNoR = "t<'row-fluid'<'span6'i><'span6'p>>"; -var sDomClean = 't'; -var sDomPage = "<'row-fluid'r>t<'row-fluid'<'span6'i><'span6'p>>"; +const sDomNoTNoR = "t<'row-fluid'<'span6'i><'span6'p>>"; +const sDomClean = 't'; +const sDomPage = "<'row-fluid'r>t<'row-fluid'<'span6'i><'span6'p>>"; /** * By default DataTables only uses the sAjaxSource variable at initialisation @@ -1690,7 +1540,7 @@ jQuery.fn.dataTableExt.oApi.fnReloadAjax = function( // 1.10's API has ajax reloading built in, so we use those abilities // directly. if (jQuery.fn.dataTable.versionCheck) { - var api = new jQuery.fn.dataTable.Api(oSettings); + const api = new jQuery.fn.dataTable.Api(oSettings); if (sNewSource) { api.ajax.url(sNewSource).load(fnCallback, !bStandingRedraw); @@ -1711,9 +1561,9 @@ jQuery.fn.dataTableExt.oApi.fnReloadAjax = function( } this.oApi._fnProcessingDisplay(oSettings, true); - var that = this; - var iStart = oSettings._iDisplayStart; - var aData = []; + const that = this; + const iStart = oSettings._iDisplayStart; + const aData = []; this.oApi._fnServerParams(oSettings, aData); @@ -1726,13 +1576,13 @@ jQuery.fn.dataTableExt.oApi.fnReloadAjax = function( that.oApi._fnClearTable(oSettings); /* Got the data - add it to the table */ - var aData = + const dataArray = oSettings.sAjaxDataProp !== '' ? that.oApi._fnGetObjectDataFn(oSettings.sAjaxDataProp)(json) : json; - var i; - for (i = 0; i < aData.length; i++) { - that.oApi._fnAddData(oSettings, aData[i]); + let i; + for (i = 0; i < dataArray.length; i += 1) { + that.oApi._fnAddData(oSettings, dataArray[i]); } oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); @@ -1748,7 +1598,7 @@ jQuery.fn.dataTableExt.oApi.fnReloadAjax = function( that.oApi._fnProcessingDisplay(oSettings, false); /* Callback user function - for event handlers etc */ - if (typeof fnCallback == 'function' && fnCallback !== null) { + if (typeof fnCallback === 'function' && fnCallback !== null) { fnCallback(oSettings); } }, diff --git a/routes/form-management.js b/routes/form-management.js index 34b1d537..14431575 100644 --- a/routes/form-management.js +++ b/routes/form-management.js @@ -1,40 +1,45 @@ -const auth = require('../lib/auth'); const mongoose = require('mongoose'); -const routesUtilities = require('../utilities/routes.js'); +const debug = require('debug')('traveler:released-form'); +const _ = require('lodash'); +const auth = require('../lib/auth'); +const routesUtilities = require('../utilities/routes'); + const Form = mongoose.model('Form'); const ReleasedForm = mongoose.model('ReleasedForm'); const { statusMap } = require('../model/released-form'); const reqUtils = require('../lib/req-utils'); const logger = require('../lib/loggers').getLogger(); -const config = require('../config/config.js'); +const config = require('../config/config'); +const { stateTransition } = require('../model/released-form'); + const authConfig = config.auth; -const debug = require('debug')('traveler:released-form'); -var _ = require('lodash'); module.exports = function(app) { - app.get('/form-management/', auth.verifyRole('manager', 'admin'), function( - req, - res - ) { - res.render('form-management', routesUtilities.getRenderObject(req)); - }); + app.get( + '/form-management/', + auth.ensureAuthenticated, + auth.verifyRole('admin'), + function(req, res) { + res.render('form-management', routesUtilities.getRenderObject(req)); + } + ); app.get( '/submitted-forms/json', auth.ensureAuthenticated, - auth.verifyRole('admin', 'manager'), + auth.verifyRole('admin'), function(req, res) { Form.find( { status: 0.5, }, - 'title formType status tags mapping _v updatedOn updatedBy' + 'title formType status tags mapping _v __review' ).exec(function(err, forms) { if (err) { - console.error(err); + logger.error(err); return res.status(500).send(err.message); } - res.status(200).json(forms); + return res.status(200).json(forms); }); } ); @@ -47,10 +52,10 @@ module.exports = function(app) { 'title formType status tags ver releasedOn releasedBy' ).exec(function(err, forms) { if (err) { - console.error(err); + logger.error(err); return res.status(500).send(err.message); } - res.status(200).json(forms); + return res.status(200).json(forms); }); }); @@ -65,10 +70,10 @@ module.exports = function(app) { 'title formType status tags ver archivedOn archivedBy' ).exec(function(err, forms) { if (err) { - console.error(err); + logger.error(err); return res.status(500).send(err.message); } - res.status(200).json(forms); + return res.status(200).json(forms); }); }); @@ -85,7 +90,7 @@ module.exports = function(app) { title: releasedForm.title, formType: releasedForm.formType, status: releasedForm.status, - statusText: statusMap['' + releasedForm.status], + statusText: statusMap[`${releasedForm.status}`], ver: releasedForm.ver, base: releasedForm.base, discrepancy: releasedForm.discrepancy, @@ -94,61 +99,24 @@ module.exports = function(app) { } ); - /** - * Batch archiving of multiple released forms - */ - app.put( - '/released-forms/archive', - auth.ensureAuthenticated, - auth.verifyRole('admin', 'manager'), - function updateStatus(req, res) { - let ids = req.body; - if (!ids || ids.length < 1) { - return res.status(400).send('no id(s) provided'); - } - - let stateTransition = require('../model/released-form').stateTransition; - - ids.forEach(function(id) { - ReleasedForm.findById({ _id: id }, function(err, f) { - if (err) { - return res.status(400).send('Invalid id: ' + id); - } - f.status = 2; - f.archivedBy = req.session.userid; - f.archivedOn = Date.now(); - // check if we need to increment the version - // in this case, no - f.incrementVersion(); - f.saveWithHistory(req.session.userid) - .then(function() {}) - .catch(function(err) { - return res.status(500).send('Failed to archive form: ' + id); - }); - }); - }); - return res.status(200).send('Archived form(s): ' + ids.join(', ')); - } - ); - app.put( '/released-forms/:id/status', auth.ensureAuthenticated, - auth.verifyRole('admin', 'manager'), reqUtils.exist('id', ReleasedForm), + reqUtils.isOwnerOrAdminMw('id'), reqUtils.filter('body', ['status', 'version']), reqUtils.hasAll('body', ['status', 'version']), - function updateStatus(req, res) { - var f = req[req.params.id]; - var s = req.body.status; - var v = req.body.version; + async function updateStatus(req, res) { + const f = req[req.params.id]; + const s = req.body.status; + const v = req.body.version; if ([2].indexOf(s) === -1) { return res.status(400).send('invalid status'); } if (v !== f.ver) { - return res.status(400).send('the current version is ' + f.ver); + return res.status(400).send(`the current version is ${f.ver}`); } // no change @@ -156,9 +124,7 @@ module.exports = function(app) { return res.status(204).send(); } - var stateTransition = require('../model/released-form').stateTransition; - - var target = _.find(stateTransition, function(t) { + const target = _.find(stateTransition, function(t) { return t.from === f.status; }); @@ -175,13 +141,14 @@ module.exports = function(app) { // check if we need to increment the version // in this case, no f.incrementVersion(); - f.saveWithHistory(req.session.userid) - .then(function() { - return res.status(200).send('status updated to ' + s); - }) - .catch(function(err) { - return res.status(500).send(err.message); - }); + try { + await f.saveWithHistory(req.session.userid); + return res + .status(200) + .send(`released form ${req.params.id} status updated to ${s}`); + } catch (error) { + return res.status(500).send(error.message); + } } ); @@ -191,7 +158,7 @@ module.exports = function(app) { reqUtils.exist('id', ReleasedForm), function(req, res) { const releasedForm = req[req.params.id]; - const base = releasedForm.base; + const { base } = releasedForm; const clonedForm = {}; clonedForm.html = reqUtils.sanitizeText(base.html); clonedForm.title = reqUtils.sanitizeText(req.body.title); @@ -199,7 +166,7 @@ module.exports = function(app) { clonedForm.createdOn = Date.now(); clonedForm.updatedBy = req.session.userid; clonedForm.updatedOn = Date.now(); - clonedForm.clonedFrom = base.reference; + clonedForm.clonedFrom = base._id; clonedForm.formType = base.formType; clonedForm.sharedWith = []; clonedForm.tags = releasedForm.tags; @@ -208,17 +175,13 @@ module.exports = function(app) { logger.error(saveErr); return res.status(500).send(saveErr.message); } - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/forms/' + - newform.id + - '/'; + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/forms/${newform.id}/`; res.set('Location', url); return res .status(201) - .send( - 'You can see the new form at ' + url + '' - ); + .send(`You can see the new form at ${url}`); }); } ); @@ -238,7 +201,7 @@ module.exports = function(app) { console.error(err); return res.status(500).send(err.message); } - res.status(200).json(forms); + return res.status(200).json(forms); }); }); @@ -257,7 +220,7 @@ module.exports = function(app) { console.error(err); return res.status(500).send(err.message); } - res.status(200).json(forms); + return res.status(200).json(forms); }); } ); diff --git a/routes/form.js b/routes/form.js index 4d621d7b..01ab1279 100644 --- a/routes/form.js +++ b/routes/form.js @@ -1,111 +1,178 @@ -var config = require('../config/config.js'); -var auth = require('../lib/auth'); -var authConfig = config.auth; - -var mongoose = require('mongoose'); -var path = require('path'); -var _ = require('lodash'); -var routesUtilities = require('../utilities/routes.js'); -var reqUtils = require('../lib/req-utils'); -var shareLib = require('../lib/share'); -var tag = require('../lib/tag'); -var FormError = require('../lib/error').FormError; -var formModel = require('../model/form'); - -var Form = mongoose.model('Form'); -var FormFile = mongoose.model('FormFile'); +const debug = require('debug')('traveler:route:form'); +const _ = require('lodash'); +const mongoose = require('mongoose'); +const path = require('path'); +const config = require('../config/config'); +const auth = require('../lib/auth'); + +const authConfig = config.auth; + +const routesUtilities = require('../utilities/routes'); +const reqUtils = require('../lib/req-utils'); +const shareLib = require('../lib/share'); +const tag = require('../lib/tag'); +const formModel = require('../model/form'); +const reviewLib = require('../lib/review'); + +const Form = mongoose.model('Form'); +const FormFile = mongoose.model('FormFile'); const ReleasedForm = mongoose.model('ReleasedForm'); const FormContent = mongoose.model('FormContent'); -var User = mongoose.model('User'); -var Group = mongoose.model('Group'); +const User = mongoose.model('User'); +const Group = mongoose.model('Group'); +const { stateTransition } = require('../model/form'); + +const logger = require('../lib/loggers').getLogger(); -var debug = require('debug')('traveler:route:form'); -var logger = require('../lib/loggers').getLogger(); +function checkReviewer(form, userid) { + return ( + form.__review && + form.__review.reviewRequests && + form.__review.reviewRequests.id(userid) + ); +} module.exports = function(app) { app.get('/forms/', auth.ensureAuthenticated, function(req, res) { res.render('forms', routesUtilities.getRenderObject(req)); }); - app.get('/forms/json', auth.ensureAuthenticated, function(req, res) { - Form.find( - { - createdBy: req.session.userid, - archived: { - $ne: true, + app.get('/releasedforms/', auth.ensureAuthenticated, function(req, res) { + res.render('released-forms', routesUtilities.getRenderObject(req)); + }); + + app.get('/forms/json', auth.ensureAuthenticated, async function(req, res) { + try { + const forms = await Form.find( + { + createdBy: req.session.userid, + archived: { + $ne: true, + }, + status: 0, + owner: { + $exists: false, + }, }, - status: { - $ne: 2, + 'title formType status tags mapping createdBy createdOn updatedBy updatedOn publicAccess sharedWith sharedGroup _v' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } + }); + + // forms owned by the user that are under review + app.get('/submittedforms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + try { + const forms = await Form.find( + { + createdBy: req.session.userid, + archived: { + $ne: true, + }, + status: { + $in: [0.5], + }, + owner: { + $exists: false, + }, }, - owner: { - $exists: false, + 'title formType status tags mapping createdBy createdOn updatedBy updatedOn publicAccess sharedWith sharedGroup _v' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } + }); + + // forms owned by the user that are under review + app.get('/closedforms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + try { + const forms = await Form.find( + { + createdBy: req.session.userid, + archived: { + $ne: true, + }, + status: { + $in: [1], + }, + owner: { + $exists: false, + }, }, - }, - 'title formType status tags mapping createdBy createdOn updatedBy updatedOn publicAccess sharedWith sharedGroup _v' - ).exec(function(err, forms) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - res.status(200).json(forms); - }); + 'title formType status tags mapping createdBy createdOn updatedBy updatedOn publicAccess sharedWith sharedGroup _v' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } }); - app.get('/transferredforms/json', auth.ensureAuthenticated, function( + app.get('/transferredforms/json', auth.ensureAuthenticated, async function( req, res ) { - Form.find( - { - owner: req.session.userid, - archived: { - $ne: true, + try { + const forms = await Form.find( + { + owner: req.session.userid, + archived: { + $ne: true, + }, }, - }, - 'title formType status tags createdBy createdOn updatedBy updatedOn transferredOn publicAccess sharedWith sharedGroup' - ).exec(function(err, forms) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - res.status(200).json(forms); - }); + 'title formType status tags createdBy createdOn updatedBy updatedOn transferredOn publicAccess sharedWith sharedGroup' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } }); - app.get('/allforms/json', auth.ensureAuthenticated, function(req, res) { - if (routesUtilities.checkUserRole(req, 'read_all_forms')) { - Form.find( + app.get('/allforms/json', auth.ensureAuthenticated, async function(req, res) { + if (!routesUtilities.checkUserRole(req, 'read_all_forms')) { + return res.status(401).json('You are not authorized to view all forms.'); + } + try { + const forms = await Form.find( {}, 'title formType status tags createdBy createdOn updatedBy updatedOn sharedWith sharedGroup' ) .lean() - .exec(function(err, forms) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - res.status(200).json(forms); - }); - } else { - res.status(200).json('You are not authorized to view all forms.'); + .exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); } }); - app.get('/sharedforms/json', auth.ensureAuthenticated, function(req, res) { - User.findOne( - { - _id: req.session.userid, - }, - 'forms' - ).exec(function(err, me) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } + app.get('/sharedforms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + try { + const me = await User.findOne( + { + _id: req.session.userid, + }, + 'forms' + ).exec(); if (!me) { return res.status(400).send('cannot identify the current user'); } - Form.find( + const forms = await Form.find( { _id: { $in: me.forms, @@ -115,35 +182,30 @@ module.exports = function(app) { }, }, 'title formType status tags owner updatedBy updatedOn publicAccess sharedWith sharedGroup' - ).exec(function(fErr, forms) { - if (fErr) { - logger.error(fErr); - return res.status(500).send(fErr.message); - } - res.status(200).json(forms); - }); - }); + ).exec(); + return res.status(200).json(forms); + } catch (error) { + debug(`error: ${error}`); + return res.status(500).send(error.message); + } }); - app.get('/groupsharedforms/json', auth.ensureAuthenticated, function( + app.get('/groupsharedforms/json', auth.ensureAuthenticated, async function( req, res ) { - Group.find( - { - _id: { - $in: req.session.memberOf, + try { + const groups = await Group.find( + { + _id: { + $in: req.session.memberOf, + }, }, - }, - 'forms' - ).exec(function(err, groups) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - var formids = []; - var i; - var j; + 'forms' + ).exec(); + const formids = []; + let i; + let j; // merge the forms arrays for (i = 0; i < groups.length; i += 1) { for (j = 0; j < groups[i].forms.length; j += 1) { @@ -152,7 +214,7 @@ module.exports = function(app) { } } } - Form.find( + const forms = await Form.find( { _id: { $in: formids, @@ -162,18 +224,19 @@ module.exports = function(app) { }, }, 'title formType status tags owner updatedBy updatedOn publicAccess sharedWith sharedGroup' - ).exec(function(fErr, forms) { - if (fErr) { - logger.error(fErr); - return res.status(500).send(fErr.message); - } - res.status(200).json(forms); - }); - }); + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } }); - app.get('/archivedforms/json', auth.ensureAuthenticated, function(req, res) { - var search = { + app.get('/archivedforms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + const search = { $and: [ { $or: [ @@ -200,36 +263,40 @@ module.exports = function(app) { }, ], }; - Form.find(search, 'title formType status tags updatedBy updatedOn _v').exec( - function(err, forms) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - res.status(200).json(forms); - } - ); + try { + const forms = await Form.find( + search, + 'title formType status tags updatedBy updatedOn _v' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } }); app.get('/publicforms/', auth.ensureAuthenticated, function(req, res) { res.render('public-forms', routesUtilities.getRenderObject(req)); }); - app.get('/publicforms/json', auth.ensureAuthenticated, function(req, res) { - Form.find({ - publicAccess: { - $in: [0, 1], - }, - archived: { - $ne: true, - }, - }).exec(function(err, forms) { - if (err) { - logger.error(err); - return res.status(500).send(err.message); - } - res.status(200).json(forms); - }); + app.get('/publicforms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + try { + const forms = await Form.find({ + publicAccess: { + $in: [0, 1], + }, + archived: { + $ne: true, + }, + }).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } }); app.get('/forms/new', auth.ensureAuthenticated, function(req, res) { @@ -241,8 +308,8 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), function formBuilder(req, res) { - var form = req[req.params.id]; - var access = reqUtils.getAccess(req, form); + const form = req[req.params.id]; + const access = reqUtils.getAccess(req, form); if (access === -1) { return res @@ -252,14 +319,17 @@ module.exports = function(app) { if (form.archived) { return res.redirect( - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/forms/' + - req.params.id + - '/preview' + `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/forms/${req.params.id}/preview` ); } - if (access === 1) { + const isReviewer = checkReviewer(form, req.session.userid); + const allApproved = form.allApproved(); + debug(`all approved: ${allApproved}`); + + if (access === 1 && form.isBuilder()) { return res.render( 'form-builder', routesUtilities.getRenderObject(req, { @@ -267,20 +337,22 @@ module.exports = function(app) { title: form.title, html: form.html, status: form.status, - statusText: formModel.statusMap['' + form.status], + statusText: formModel.statusMap[`${form.status}`], _v: form._v, formType: form.formType, prefix: req.proxied ? req.proxied_prefix : '', + isReviewer, + allApproved, + review: form.__review, released_form_version_mgmt: config.app.released_form_version_mgmt, }) ); } return res.redirect( - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/forms/' + - req.params.id + - '/preview' + `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/forms/${req.params.id}/preview` ); } ); @@ -300,26 +372,21 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.canReadMw('id'), - function(req, res) { + async function(req, res) { + const baseIds = [req.params.id]; + const parentFormId = req[req.params.id].clonedFrom; + if (parentFormId) { + baseIds.push(parentFormId); + } + try { - ReleasedForm.find( - { - 'base._id': req.params.id, - status: 1, // released + const forms = await ReleasedForm.find({ + 'base._id': { + $in: baseIds, }, - function(err, existingForms) { - if (err) { - return res.status(500).send(error.message); - } - debug( - 'found ' + - existingForms.length + - ' previously released form(s) based on : ' + - req.params.id - ); - return res.status(200).json(existingForms); - } - ); + status: 1, // released + }).exec(); + return res.status(200).json(forms); } catch (error) { return res.status(500).send(error.message); } @@ -331,8 +398,8 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.canWriteMw('id'), - function(req, res) { - var doc = req[req.params.id]; + async function(req, res) { + const doc = req[req.params.id]; if (_.isEmpty(req.files)) { return res.status(400).send('Expect One uploaded file'); } @@ -341,7 +408,7 @@ module.exports = function(app) { return res.status(400).send('Expect input name'); } - var file = new FormFile({ + const file = new FormFile({ form: doc._id, value: req.files[req.body.name].originalname, file: { @@ -353,27 +420,21 @@ module.exports = function(app) { uploadedBy: req.session.userid, uploadedOn: Date.now(), }); - - file.save(function(saveErr, newfile) { - if (saveErr) { - logger.error(saveErr); - return res.status(500).send(saveErr.message); - } - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/formfiles/' + - newfile.id; + try { + const newFile = await file.save(); + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/formfiles/${newFile.id}`; res.set('Location', url); return res .status(201) .send( - 'The uploaded file is at ' + - url + - '' + `The uploaded file is at ${url}` ); - }); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } } ); @@ -382,7 +443,7 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', FormFile), function(req, res) { - var data = req[req.params.id]; + const data = req[req.params.id]; if (data.inputType === 'file') { return res.sendFile(path.resolve(data.file.path)); } @@ -396,7 +457,7 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.canReadMw('id'), function(req, res) { - var form = req[req.params.id]; + const form = req[req.params.id]; return res.render( 'form-viewer', routesUtilities.getRenderObject(req, { @@ -415,7 +476,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.archived('id', false), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; return res.render( 'form-config', routesUtilities.getRenderObject(req, { @@ -432,7 +493,7 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), function(req, res) { - var form = req[req.params.id]; + const form = req[req.params.id]; return res.render( 'share', routesUtilities.getRenderObject(req, { @@ -445,16 +506,86 @@ module.exports = function(app) { } ); + app.get( + '/forms/:id/review/', + auth.ensureAuthenticated, + reqUtils.exist('id', Form), + reqUtils.isOwnerMw('id'), + function(req, res) { + const form = req[req.params.id]; + return res.render( + 'review', + routesUtilities.getRenderObject(req, { + type: 'form', + id: req.params.id, + title: form.title, + }) + ); + } + ); + + app.get( + '/forms/:id/review/json', + auth.ensureAuthenticated, + reqUtils.exist('id', Form), + reqUtils.isOwnerMw('id'), + function(req, res) { + const form = req[req.params.id]; + return res.status(200).json(form.__review || {}); + } + ); + + // add a new review request + app.post( + '/forms/:id/review/requests', + auth.ensureAuthenticated, + reqUtils.exist('id', Form), + reqUtils.isOwnerMw('id'), + async function(req, res) { + const form = req[req.params.id]; + await reviewLib.addReviewRequest(req, res, form); + } + ); + + // remove a review request + app.delete( + '/forms/:id/review/requests/:requestId', + reqUtils.exist('id', Form), + reqUtils.isOwnerMw('id'), + auth.ensureAuthenticated, + async function(req, res) { + const form = req[req.params.id]; + await reviewLib.removeReviewRequest(req, res, form); + } + ); + + // add a new review request + app.post( + '/forms/:id/review/results', + auth.ensureAuthenticated, + reqUtils.exist('id', Form), + function(req, res, next) { + const isReviewer = checkReviewer(req[req.params.id], req.session.userid); + if (!isReviewer) { + return res.status(401).send('only reviewer can submit'); + } + return next(); + }, + async function(req, res) { + const form = req[req.params.id]; + await reviewLib.addReviewResult(req, res, form); + } + ); + app.put( '/forms/:id/share/public', auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), reqUtils.filter('body', ['access']), - function(req, res) { - var form = req[req.params.id]; - // change the access - var access = req.body.access; + async function(req, res) { + const form = req[req.params.id]; + let { access } = req.body; if (['-1', '0', '1'].indexOf(access) === -1) { return res.status(400).send('not valid value'); } @@ -463,15 +594,15 @@ module.exports = function(app) { return res.status(204).send(); } form.publicAccess = access; - form.save(function(saveErr) { - if (saveErr) { - logger.error(saveErr); - return res.status(500).send(saveErr.message); - } + try { + await form.save(); return res .status(200) - .send('public access is set to ' + req.body.access); - }); + .send(`public access is set to ${req.body.access}`); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } } ); @@ -481,7 +612,7 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), function(req, res) { - var form = req[req.params.id]; + const form = req[req.params.id]; if (req.params.list === 'users') { return res.status(200).json(form.sharedWith || []); } @@ -498,8 +629,8 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), function(req, res) { - var form = req[req.params.id]; - var share = -2; + const form = req[req.params.id]; + let share = -2; if (req.params.list === 'users') { if (req.body.name) { share = reqUtils.getSharedWith(form.sharedWith, req.body.name); @@ -524,14 +655,12 @@ module.exports = function(app) { .status(400) .send( req.body.name || - req.body.id + ' is already in the ' + req.params.list + ' list.' + `${req.body.id} is already in the ${req.params.list} list.` ); } - if (share === -1) { - // new user - shareLib.addShare(req, res, form); - } + // share === -1 + return shareLib.addShare(req, res, form); } ); @@ -541,9 +670,9 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), reqUtils.filter('body', ['access']), - function(req, res) { - var form = req[req.params.id]; - var share; + async function(req, res) { + const form = req[req.params.id]; + let share; if (req.params.list === 'users') { share = form.sharedWith.id(req.params.shareid); } @@ -553,7 +682,7 @@ module.exports = function(app) { if (!share) { return res .status(400) - .send('cannot find ' + req.params.shareid + ' in the list.'); + .send(`cannot find ${req.params.shareid} in the list.`); } // change the access if (req.body.access === 'write') { @@ -563,41 +692,34 @@ module.exports = function(app) { } else { return res .status(400) - .send('cannot take the access ' + req.body.access); + .send(`cannot take the access ${req.body.access}`); } - form.save(function(saveErr) { - if (saveErr) { - logger.error(saveErr); - return res.status(500).send(saveErr.message); - } + try { + await form.save(); // check consistency of user's form list - var Target; + let Target; if (req.params.list === 'users') { Target = User; } if (req.params.list === 'groups') { Target = Group; } - Target.findByIdAndUpdate( - req.params.shareid, - { - $addToSet: { - forms: form._id, - }, + const newTarget = await Target.findByIdAndUpdate(req.params.shareid, { + $addToSet: { + forms: form._id, }, - function(updateErr, target) { - if (updateErr) { - logger.error(updateErr); - } - if (!target) { - logger.error( - 'The user/group ' + req.params.userid + ' is not in the db' - ); - } - } - ); + }); + if (!newTarget) { + logger.error(`The user/group ${req.params.shareid} is not in the db`); + return res + .status(404) + .send(`user/group ${req.params.shareid} not found`); + } return res.status(200).json(share); - }); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } } ); @@ -607,7 +729,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), auth.ensureAuthenticated, function(req, res) { - var form = req[req.params.id]; + const form = req[req.params.id]; shareLib.removeShare(req, res, form); } ); @@ -619,30 +741,28 @@ module.exports = function(app) { reqUtils.hasAll('body', ['title']), auth.requireRoles(req => { return ( - req['body'].hasOwnProperty('formType') && - req['body']['formType'] == 'discrepancy' + req.body.hasOwnProperty('formType') && + req.body.formType === 'discrepancy' ); }, 'admin'), reqUtils.sanitize('body', ['html']), function(req, res) { - var html = req.body.html || ''; + const html = req.body.html || ''; formModel.createForm( { title: req.body.title, formType: req.body.formType, createdBy: req.session.userid, - html: html, + html, }, function(err, newform) { if (err) { logger.error(err); return res.status(500).send(err.message); } - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/forms/' + - newform.id + - '/'; + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/forms/${newform.id}/`; res.set('Location', url); return res.status(303).json({ @@ -659,8 +779,8 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.canReadMw('id'), function(req, res) { - var doc = req[req.params.id]; - var form = {}; + const doc = req[req.params.id]; + const form = {}; form.html = reqUtils.sanitizeText(doc.html); form.title = reqUtils.sanitizeText(req.body.title); form.createdBy = req.session.userid; @@ -677,17 +797,13 @@ module.exports = function(app) { logger.error(saveErr); return res.status(500).send(saveErr.message); } - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/forms/' + - newform.id + - '/'; + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/forms/${newform.id}/`; res.set('Location', url); return res .status(201) - .send( - 'You can see the new form at ' + url + '' - ); + .send(`You can see the new form at ${url}`); }); } ); @@ -698,8 +814,8 @@ module.exports = function(app) { reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), reqUtils.filter('body', ['archived']), - function(req, res) { - var doc = req[req.params.id]; + async function(req, res) { + const doc = req[req.params.id]; if (doc.archived === req.body.archived) { return res.status(204).send(); } @@ -708,21 +824,17 @@ module.exports = function(app) { if (doc.archived) { doc.archivedOn = Date.now(); } - - doc.save(function(saveErr, newDoc) { - if (saveErr) { - logger.error(saveErr); - return res.status(500).send(saveErr.message); - } + try { + const newDoc = await doc.save(); return res .status(200) .send( - 'Form ' + - req.params.id + - ' archived state set to ' + - newDoc.archived + `Form ${req.params.id} archived state set to ${newDoc.archived}` ); - }); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } } ); @@ -733,7 +845,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.filter('body', ['name']), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; shareLib.changeOwner(req, res, doc); } ); @@ -743,14 +855,14 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.canWriteMw('id'), - reqUtils.status('id', [0]), + reqUtils.status('id', [0, 0.5]), reqUtils.filter('body', ['html', 'title', 'description']), reqUtils.sanitize('body', ['html', 'title', 'description']), - function(req, res) { + async function(req, res) { if (!req.is('json')) { return res.status(415).send('json request expected'); } - var doc = req[req.params.id]; + const doc = req[req.params.id]; if (req.body.hasOwnProperty('html')) { doc.html = req.body.html; } @@ -773,20 +885,12 @@ module.exports = function(app) { doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); doc.incrementVersion(); - doc - .saveWithHistory(req.session.userid) - .then(function(newDoc) { - return res.json(newDoc); - }) - .catch(function(saveErr) { - if (saveErr) { - logger.error(saveErr.message); - if (saveErr instanceof FormError) { - return res.status(saveErr.status).send(saveErr.message); - } - return res.status(500).send(saveErr.message); - } - }); + try { + const newDoc = await doc.saveWithHistory(req.session.userid); + return res.json(newDoc); + } catch (error) { + return res.status(500).send(error.message); + } } ); @@ -797,18 +901,25 @@ module.exports = function(app) { app.put( '/forms/:id/released', auth.ensureAuthenticated, - // only admin or manager can release a form - auth.verifyRole('admin', 'manager'), // find the unreleased form reqUtils.exist('id', Form), - // the form was submitted for release + // owner decide if release + reqUtils.isOwnerMw('id'), function(req, res, next) { if (req[req.params.id].status !== 0.5) { return res .status(400) - .send(`${req[req.params.id].id} is not submitted for release`); + .send(`${req[req.params.id].id} has not been submitted for review`); } - next(); + return next(); + }, + function(req, res, next) { + if (!req[req.params.id].allApproved) { + return res + .status(400) + .send(`${req[req.params.id].id} was not approved by all reviewers`); + } + return next(); }, // if the base form is normal then load the released discrepancy form function(req, res, next) { @@ -849,7 +960,7 @@ module.exports = function(app) { .status(400) .send(`${req[req.body.discrepancyFormId].id} is not released`); } - next(); + return next(); }, async function releaseForm(req, res) { const releasedForm = {}; @@ -869,8 +980,7 @@ module.exports = function(app) { } releasedForm.releasedBy = req.session.userid; releasedForm.releasedOn = Date.now(); - // reset the submitted form - form.status = 0; + // check if there is already a released form with the same name and // version try { @@ -882,7 +992,7 @@ module.exports = function(app) { // remove this condition if including the archive released form status: 1, }); - debug('find existing form: ' + existingForm); + debug(`find existing form: ${existingForm}`); if (existingForm) { return res .status(400) @@ -890,16 +1000,19 @@ module.exports = function(app) { `A form with same title, type, and version was already released in ${existingForm._id}.` ); } - } catch (error) { - return res.status(500).send(error.message); - } - - try { const saveForm = await new ReleasedForm(releasedForm).save(); - const url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - `/released-forms/${saveForm._id}/`; - form.save(); + + // update the form status + form.status = 1; + form.updatedBy = req.session.userid; + form.updatedOn = Date.now(); + await form.save(); + + // close the review requests + await form.closeReviewRequests(); + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/released-forms/${saveForm._id}/`; return res.status(201).json({ location: url, }); @@ -916,17 +1029,17 @@ module.exports = function(app) { reqUtils.filter('body', ['status', 'version']), reqUtils.hasAll('body', ['status', 'version']), reqUtils.canWriteMw('id'), - function updateStatus(req, res) { - var f = req[req.params.id]; - var s = req.body.status; - var v = req.body.version; + async function updateStatus(req, res) { + const f = req[req.params.id]; + const s = req.body.status; + const v = req.body.version; if ([0, 0.5, 1, 2].indexOf(s) === -1) { return res.status(400).send('invalid status'); } if (v !== f._v) { - return res.status(400).send('the current version is ' + f._v); + return res.status(400).send(`the current version is ${f._v}`); } // no change @@ -934,9 +1047,7 @@ module.exports = function(app) { return res.status(204).send(); } - var stateTransition = require('../model/form').stateTransition; - - var target = _.find(stateTransition, function(t) { + const target = _.find(stateTransition, function(t) { return t.from === f.status; }); @@ -951,13 +1062,12 @@ module.exports = function(app) { // check if we need to increment the version // in this case, no f.incrementVersion(); - f.saveWithHistory(req.session.userid) - .then(function() { - return res.status(200).send('status updated to ' + s); - }) - .catch(function(err) { - return res.status(500).send(err.message); - }); + try { + await f.saveWithHistory(req.session.userid); + return res.status(200).send(`status updated to ${s}`); + } catch (error) { + return res.status(500).send(error.message); + } } ); }; diff --git a/routes/review.js b/routes/review.js new file mode 100644 index 00000000..84e2f62e --- /dev/null +++ b/routes/review.js @@ -0,0 +1,57 @@ +const debug = require('debug')('traveler:route:review'); +const mongoose = require('mongoose'); +const auth = require('../lib/auth'); +const routesUtilities = require('../utilities/routes'); + +const User = mongoose.model('User'); +const Form = mongoose.model('Form'); + +module.exports = function(app) { + app.get('/reviews/', auth.ensureAuthenticated, function(req, res) { + if ( + req.session.roles === undefined || + req.session.roles.indexOf('manager') === -1 + ) { + return res.status(403).send('only manager allowed'); + } + return res.render('reviews', routesUtilities.getRenderObject(req)); + }); + + app.get('/reviews/forms/json', auth.ensureAuthenticated, async function( + req, + res + ) { + if ( + req.session.roles === undefined || + req.session.roles.indexOf('manager') === -1 + ) { + return res.status(403).send('only manager allowed'); + } + try { + const me = await User.findOne( + { + _id: req.session.userid, + }, + 'reviews' + ).exec(); + if (!me) { + return res.status(400).send('cannot identify the current user'); + } + const forms = await Form.find( + { + _id: { + $in: me.reviews, + }, + archived: { + $ne: true, + }, + }, + 'title formType status tags _v __review' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + debug(`error: ${error}`); + return res.status(500).send(error.message); + } + }); +}; diff --git a/views/form-builder.jade b/views/form-builder.jade index ddb3c52c..dce6567d 100644 --- a/views/form-builder.jade +++ b/views/form-builder.jade @@ -5,7 +5,7 @@ block head if (typeof id == 'undefined') title #{orgName} - new form else - title #{orgName} - forms - #{title} + title #{orgName} - form - #{title} meta(name='viewport', content='width=device-width, initial-scale=1.0') link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap.css') link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap-responsive.css') @@ -29,17 +29,65 @@ block content .modal-body .modal-footer h3#formtitle #{title} - p Type: + p Type: |  #{formType} p Status: |  #{statusText} p Version:  span#version #{_v} + if ((status === 0 || status === 0.5 || status === 1 ) && review) + h4 Reviews + if (allApproved) + p All reviewers has approved, the form is ready for release + #reviews.accordion + .accordion-group + .accordion-heading + a.accordion-toggle(data-toggle="collapse" data-parent="#reviews" href="#review-results") Reported reviews + #review-results.accordion-body.collapse.in + .accordion-inner + each result in review.reviewResults + accordion(id="#{result._id}") + .accordion-group + .accordion-heading + a.accordion-toggle(data-toggle="collapse" data-parent="#{result._id}" href="\##{result._id}-comment") #{result.reviewerId} + |   + if (result.result === "1") + b approved + else + b requested for change (review request will be closed) + |  on  + span.time #{result.submittedOn} + |  for version #{result.v} + .accordion-body.collapse(id="#{result._id}-comment") + .accordion-inner + span #{result.comment} + if (isReviewer) + .accordion-group + .accordion-heading + a.accordion-toggle(data-toggle="collapse" data-parent="#reviews" href="#new-review") Add a new review + #new-review.accordion-body.collapse + .accordion-inner + form.form-horizontal#review + .control-group + .controls + label.radio + input#approve(type="radio" name="result" value="1") + span Approve + label.radio + input#non-approve(type="radio" name="result" value="2") + span Rework + .control-group + .control-label comment + .controls + textarea#comment(name="comment", cols="40", rows="10") + .btn-group + button.btn.btn-primary#submit-review(data-toggle='tooltip', title='Report review result') Report + .well-large form.form-horizontal#output != html .button-footer.container - if (status === 0) + if (status === 0 || status === 0.5) .btn-group.dropup button.btn.dropdown-toggle(data-toggle='dropdown')#input-items Basic inputs span.caret @@ -76,25 +124,22 @@ block content button.btn.btn-primary#save Save .btn-group a.btn.btn-info#preview(data-toggle='tooltip', title='the form needs be saved first', href='preview', target='#{viewConfig.linkTarget}') Preview - .btn-group - button.btn.btn-primary#numbering(data-toggle='tooltip', title='generate section numbers') Generate numbering .btn-group button.btn.btn-primary#saveas(data-toggle='tooltip', title='create a copy of current form') Save as - if (status === 0) + if (status === 0 || status === 0.5 && !isReviewer) + .btn-group + button.btn.btn-primary#numbering(data-toggle='tooltip', title='generate section numbers') Generate numbering .btn-group button.btn.btn-warning#archive(data-toggle='tooltip', title='archive the form') Archive - if (status === 0) - .btn-group - button.btn.btn-primary#submit(data-toggle='tooltip', title='Submit for Release') Submit - if (locals.session && locals.session.userid && locals.session.roles && (locals.session.roles.indexOf('admin') !== -1 || locals.session.roles.indexOf('manager') !== -1)) - if (status === 0.5) - .btn-group - button.btn.btn-primary#release(data-toggle='tooltip', title='release the form for usage') Release - .btn-group - button.btn.btn-warning#reject(data-toggle='tooltip', title='reject the submitted form for release') Reject - if (status === 1) - .btn-group - button.btn.btn-primary#obsolete(data-toggle='tooltip', title='Obsolete the form') Obsolete + if (status === 0 && !isReviewer) + .btn-group + button.btn.btn-primary#submit(data-toggle='tooltip', title='Submit for review') Submit for review + if (status === 0.5 && !isReviewer && allApproved) + .btn-group + button.btn.btn-primary#release(data-toggle='tooltip', title='Release') Release + if (status === 1 && locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('admin') !== -1) + .btn-group + button.btn.btn-primary#obsolete(data-toggle='tooltip', title='Obsolete the form') Obsolete block js script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') diff --git a/views/form-management.jade b/views/form-management.jade index 8865eaa5..a4312f17 100644 --- a/views/form-management.jade +++ b/views/form-management.jade @@ -35,7 +35,7 @@ block content div ul.nav.nav-tabs li.active - a(href='#submitted-forms', data-toggle='tab') Submitted #{viewConfig.terminology.form}s + a(href='#submitted-forms', data-toggle='tab') Submitted #{viewConfig.terminology.form}s for review li a(href='#released-forms', data-toggle='tab') Released #{viewConfig.terminology.form}s li diff --git a/views/forms.jade b/views/forms.jade index 4381a96d..165ac942 100644 --- a/views/forms.jade +++ b/views/forms.jade @@ -30,7 +30,7 @@ block content button.btn.btn-info#reload i.fa.fa-refresh.fa-lg |  Reload all tables - if (locals.session && locals.session.userid && locals.session.roles && (locals.session.roles.indexOf('admin') !== -1 || locals.session.roles.indexOf('manager') !== -1)) + if (locals.session && locals.session.userid && locals.session.roles && (locals.session.roles.indexOf('admin') !== -1)) .btn-group a.btn.btn-info#form-management(href='#{prefix}/form-management/', target='#{viewConfig.linkTarget}', data-toggle='tooltip', title='manage #{viewConfig.terminology.form}s') i.fa.fa-suitcase.fa-lg @@ -55,11 +55,11 @@ block content li a(href='#groupsharedfroms', data-toggle='tab') Group shared draft #{viewConfig.terminology.form}s li - a(href='#releasedforms', data-toggle='tab') Released #{viewConfig.terminology.form}s + a(href='#submittedforms', data-toggle='tab') Under review #{viewConfig.terminology.form}s li - a(href='#archivedforms', data-toggle='tab') Archived draft #{viewConfig.terminology.form}s + a(href='#closedforms', data-toggle='tab') Closed #{viewConfig.terminology.form} reviews li - a(href='#archived-released-forms', data-toggle='tab') Archived released #{viewConfig.terminology.form}s + a(href='#archivedforms', data-toggle='tab') Archived draft #{viewConfig.terminology.form}s .tab-content #forms.tab-pane.active .btn-toolbar @@ -101,32 +101,29 @@ block content i.fa.fa-refresh.fa-lg |  Reload table table#group-shared-form-table.table.table-bordered.table-hover - #releasedforms.tab-pane + //- the table for under review forms + #submittedforms.tab-pane .btn-toolbar .btn-group button.btn.btn-info.reload i.fa.fa-refresh.fa-lg |  Reload table - .btn-group - button.btn.btn-primary#form-travel(data-toggle='tooltip', title='create travelers from selected #{viewConfig.terminology.form}') - i.fa.fa-plane.fa-lg - |  Travel - table#released-form-table.table.table-bordered.table-hover - #archivedforms.tab-pane + table#submitted-form-table.table.table-bordered.table-hover + //- the table for forms going through review release flow + #closedforms.tab-pane .btn-toolbar .btn-group button.btn.btn-info.reload i.fa.fa-refresh.fa-lg |  Reload table - table#archived-form-table.table.table-bordered.table-hover - #archived-released-forms.tab-pane + table#closed-form-table.table.table-bordered.table-hover + #archivedforms.tab-pane .btn-toolbar .btn-group button.btn.btn-info.reload i.fa.fa-refresh.fa-lg |  Reload table - table#archived-released-form-table.table.table-bordered.table-hover - + table#archived-form-table.table.table-bordered.table-hover block js script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') diff --git a/views/layout.jade b/views/layout.jade index 969e46a0..ae26da2b 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -10,8 +10,10 @@ html var shareGroups = !{viewConfig.shareGroups}; var linkTarget = '!{viewConfig.linkTarget}' || '_blank'; var showCCDB = !{viewConfig.showCCDB}; + var userid = '!{locals.session.userid}'; - - var isAdmin = locals.session && locals.session.userid && locals.session.roles && (locals.session.roles.indexOf('admin') !== -1 ||locals.session.roles.indexOf('manager') !== -1) + - var isAdmin = locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('admin') !== -1 + - var isManager = locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('manager') !== -1 .navbar.navbar-fixed-top .navbar-inner .container-fluid @@ -32,10 +34,15 @@ html a(href=url.url, target='_blank') #{url.text} li a(href='#{prefix}/forms/') Forms + li + a(href='#{prefix}/releasedforms/') Released Forms li a(href='#{prefix}/travelers/') Travelers li a(href='#{prefix}/binders/') Binders + if (isManager) + li + a(href='#{prefix}/reviews/') Reviews li a(href='#{prefix}/docs/', target='#{viewConfig.linkTarget}') Documents if (locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('admin') !== -1) diff --git a/views/released-forms.jade b/views/released-forms.jade new file mode 100644 index 00000000..7b72ceb1 --- /dev/null +++ b/views/released-forms.jade @@ -0,0 +1,76 @@ +extends layout + +block head + title #{orgName} Forms + meta(name='viewport', content='width=device-width, initial-scale=1.0') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap.css') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap-responsive.css') + link(rel='stylesheet', href='#{prefix}/datatables/css/dataTables.bootstrap.css') + link(rel='stylesheet', href='#{prefix}/font-awesome-4.3.0/css/font-awesome.css') + link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') + +block content + script(type='text/javascript'). + var prefix = '!{prefix}'; + .container-fluid + .btn-toolbar + .btn-group + button.btn.btn-primary#clone(data-toggle='tooltip', title='clone selected #{viewConfig.terminology.form}') + i.fa.fa-copy.fa-lg + |  Clone + .btn-group + a.btn.btn-info#all-forms(href='#{prefix}/publicforms/', target='#{viewConfig.linkTarget}', data-toggle='tooltip', title='see all public #{viewConfig.terminology.form}s') + i.fa.fa-institution.fa-lg + |  All public #{viewConfig.terminology.form}s + .btn-group + button.btn.btn-info#reload + i.fa.fa-refresh.fa-lg + |  Reload all tables + #message + #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') + .modal-header + h3#modalLabel + .modal-body.modal-body-visible + .modal-footer + div + ul.nav.nav-tabs + li.active + a(href='#released', data-toggle='tab') Released #{viewConfig.terminology.form}s + li + a(href='#archived-released-forms', data-toggle='tab') Archived released #{viewConfig.terminology.form}s + .tab-content + #released.tab-pane.active + .btn-toolbar + .btn-group + button.btn.btn-info.reload + i.fa.fa-refresh.fa-lg + |  Reload table + .btn-group + button.btn.btn-primary#form-travel(data-toggle='tooltip', title='create travelers from selected #{viewConfig.terminology.form}') + i.fa.fa-plane.fa-lg + |  Travel + table#released-form-table.table.table-bordered.table-hover + #archived-released-forms.tab-pane + .btn-toolbar + .btn-group + button.btn.btn-info.reload + i.fa.fa-refresh.fa-lg + |  Reload table + table#archived-released-form-table.table.table-bordered.table-hover + + +block js + script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') + script(type='text/javascript', src='#{prefix}/datatables/js/jquery.dataTables.js') + script(type='text/javascript', src='#{prefix}/bootstrap/js/bootstrap.js') + script(type='text/javascript', src='#{prefix}/datatables/js/ZeroClipboard.js') + script(type='text/javascript', src='#{prefix}/datatables/js/TableTools.js') + script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') + script(type='text/javascript', src='#{prefix}/dependencies/moment.js') + script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/ajax-helper.js') + script(type='text/javascript', src='#{prefix}/dependencies/typeahead.bundle.js') + script(type='text/javascript', src='#{prefix}/javascripts/global.js') + script(type='text/javascript', src='#{prefix}/javascripts/usernames.js') + script(type='text/javascript', src='#{prefix}/javascripts/table-action.js') + script(type='text/javascript', src='#{prefix}/javascripts/released-forms.js') diff --git a/views/review.jade b/views/review.jade new file mode 100644 index 00000000..d15429b4 --- /dev/null +++ b/views/review.jade @@ -0,0 +1,53 @@ +extends layout + +block head + meta(charset='urf-8') + title #{orgName} - #{type} - #{id} - review + meta(name='viewport', content='width=device-width, initial-scale=1.0') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap.css') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap-responsive.css') + link(rel='stylesheet', href='#{prefix}/datatables/css/dataTables.bootstrap.css') + link(rel='stylesheet', href='#{prefix}/font-awesome-4.3.0/css/font-awesome.css') + link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') + +block content + script(type='text/javascript'). + var prefix = '!{prefix}'; + + .container(style="margin-top: 10px") + #message + #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') + .modal-header + h3#modalLabel + .modal-body + .modal-footer + h3 Reviewer list for #{type} + = ' ' + span.text-info #{title} + form.form-inline(name='user') + input#username.input(type='text', placeholder='Last, First', name='name') + != ' ' + button.btn.btn-primary#add(type='submit', disabled) Request review + .btn-toolbar + .btn-group + button.btn.btn-warning#review-remove + i.fa.fa-trash-o.fa-lg + |  Remove + table#review-table.table.table-bordered.table-hover + +block js + script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') + script(type='text/javascript', src='#{prefix}/datatables/js/jquery.dataTables.js') + script(type='text/javascript', src='#{prefix}/bootstrap/js/bootstrap.js') + script(type='text/javascript', src='#{prefix}/dependencies/moment.js') + script(type='text/javascript', src='#{prefix}/dependencies/livestamp.js') + script(type='text/javascript', src='#{prefix}/datatables/js/ZeroClipboard.js') + script(type='text/javascript', src='#{prefix}/datatables/js/TableTools.js') + script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') + script(type='text/javascript', src='#{prefix}/dependencies/typeahead.bundle.js') + script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/util.js') + script(type='text/javascript', src='#{prefix}/javascripts/ajax-helper.js') + script(type='text/javascript', src='#{prefix}/javascripts/global.js') + script(type='text/javascript', src='#{prefix}/javascripts/usernames.js') + script(type='text/javascript', src='#{prefix}/javascripts/review.js') diff --git a/views/reviews.jade b/views/reviews.jade new file mode 100644 index 00000000..aae2c3db --- /dev/null +++ b/views/reviews.jade @@ -0,0 +1,53 @@ +extends layout + +block head + title #{orgName} Reviews + meta(name='viewport', content='width=device-width, initial-scale=1.0') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap.css') + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap-responsive.css') + link(rel='stylesheet', href='#{prefix}/datatables/css/dataTables.bootstrap.css') + link(rel='stylesheet', href='#{prefix}/font-awesome-4.3.0/css/font-awesome.css') + link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') + +block content + script(type='text/javascript'). + var prefix = '!{prefix}'; + .container-fluid + #message + #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') + .modal-header + h3#modalLabel + .modal-body.modal-body-visible + .modal-footer + p + div + ul.nav.nav-tabs + li.active + a(href='#forms', data-toggle='tab') #{viewConfig.terminology.form}s + .tab-content + #forms.tab-pane.active + .btn-toolbar + .btn-group + button.btn.btn-info.reload + i.fa.fa-refresh.fa-lg + |  Reload table + table#form-table.table.table-bordered.table-hover + + +block js + script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') + script(type='text/javascript', src='#{prefix}/datatables/js/jquery.dataTables.js') + script(type='text/javascript', src='#{prefix}/bootstrap/js/bootstrap.js') + script(type='text/javascript', src='#{prefix}/datatables/js/ZeroClipboard.js') + script(type='text/javascript', src='#{prefix}/datatables/js/TableTools.js') + script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') + script(type='text/javascript', src='#{prefix}/dependencies/moment.js') + script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/ajax-helper.js') + script(type='text/javascript', src='#{prefix}/dependencies/typeahead.bundle.js') + script(type='text/javascript', src='#{prefix}/javascripts/global.js') + script(type='text/javascript', src='#{prefix}/javascripts/usernames.js') + script(type='text/javascript', src='#{prefix}/javascripts/table-action.js') + script(type='text/javascript', src='#{prefix}/javascripts/reviews.js') + + From 76bd764e3024233f0bc6bc75908b84446c8aac5a Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sat, 18 Dec 2021 09:19:14 -0800 Subject: [PATCH 06/58] 101 edit review (#102) * update only when draft * status 0 edit, 0.5 review not editable * disable release after click * show buttons for draft status, archive any status, no obsolete * count review results for current version and reviewers * fix all review --- model/form.js | 6 +++++- model/review.js | 29 +++++++++++++++++------------ public/javascripts/form-builder.js | 1 + public/javascripts/forms.js | 6 ++---- routes/form.js | 12 ++++++++++-- views/form-builder.jade | 22 +++++++++++----------- 6 files changed, 46 insertions(+), 30 deletions(-) diff --git a/model/form.js b/model/form.js index 46df67c2..4b9241aa 100644 --- a/model/form.js +++ b/model/form.js @@ -18,7 +18,7 @@ publicAccess := 0 // for read or /** **** status := 0 // editable draft | 0.5 // submitted for reviewing - | 1 // ready to release (review passed) + | 1 // review finished and released | 2 // archived ***** */ // mapping : user-key -> name @@ -164,6 +164,10 @@ form.pre('save', function(next) { return next(); }); +/** + * Check if a form should be rendered in builder + * @returns true if rendered in builder view, other wise false + */ form.methods.isBuilder = function() { const doc = this; return [0, 0.5, 1].includes(doc.status); diff --git a/model/review.js b/model/review.js index d8f9ee8d..cce92da0 100644 --- a/model/review.js +++ b/model/review.js @@ -5,6 +5,7 @@ const User = mongoose.model('User'); const { Schema } = mongoose; const debug = require('debug')('traveler:review'); +const util = require('util'); const logger = require('../lib/loggers').getLogger(); const reviewRequest = new Schema({ @@ -166,24 +167,28 @@ function addReview(schema) { if (reviewRequests.length === 0) { return false; } - const approval = {}; + const approval = new Map(); let i; debug(`has ${reviewResults.length} results`); debug(`has ${reviewRequests.length} requests`); - for (i = reviewResults.length - 1; i >= 0; i -= 1) { - debug(reviewResults[i].result); - if ( - reviewResults[i].result !== '1' && - !approval[reviewResults[i].reviewerId] - ) { - return false; - } - if (reviewResults[i].result === '1') { - approval[reviewResults[i].reviewerId] = true; + // filter to the current version + const docVersion = doc._v; + const currentReviewResults = reviewResults.filter(r => r.v === docVersion); + // the last is the latest + for (i = currentReviewResults.length - 1; i >= 0; i -= 1) { + debug( + `${i} : ${currentReviewResults[i].reviewerId} , ${currentReviewResults[i].result}` + ); + if (!approval.has(currentReviewResults[i].reviewerId)) { + approval.set( + currentReviewResults[i].reviewerId, + currentReviewResults[i].result + ); } } + debug(`filtered list ${util.inspect(approval)}`); for (i = 0; i < reviewRequests.length; i += 1) { - if (!approval[reviewRequests[i]._id]) { + if (approval.get(reviewRequests[i]._id) !== '1') { return false; } } diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index e689acb3..7029160e 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1479,6 +1479,7 @@ function binding_events() { }); $('#release').click(function() { + $('#release').attr('disabled', true); $('#modal .modal-body').empty(); const defaultTitle = $('#formtitle').text(); $('#modal .modal-body').append( diff --git a/public/javascripts/forms.js b/public/javascripts/forms.js index 15c433b6..50a6dd34 100644 --- a/public/javascripts/forms.js +++ b/public/javascripts/forms.js @@ -322,8 +322,6 @@ $(function() { const closedFormAoColumns = [ selectColumn, formLinkColumn, - formConfigLinkColumn, - formReviewLinkColumn, titleColumn, formStatusColumn, formTypeColumn, @@ -357,8 +355,8 @@ $(function() { bDeferRender: true, aoColumns: closedFormAoColumns, aaSorting: [ - [11, 'desc'], - [10, 'desc'], + [9, 'desc'], + [8, 'desc'], ], sDom: sDomNoTools, }); diff --git a/routes/form.js b/routes/form.js index 01ab1279..18b2a6da 100644 --- a/routes/form.js +++ b/routes/form.js @@ -511,6 +511,8 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), + // only available when under review + reqUtils.status('id', [0.5]), function(req, res) { const form = req[req.params.id]; return res.render( @@ -541,6 +543,8 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), + // only available when under review + reqUtils.status('id', [0.5]), async function(req, res) { const form = req[req.params.id]; await reviewLib.addReviewRequest(req, res, form); @@ -550,9 +554,11 @@ module.exports = function(app) { // remove a review request app.delete( '/forms/:id/review/requests/:requestId', + auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.isOwnerMw('id'), - auth.ensureAuthenticated, + // only available when under review + reqUtils.status('id', [0.5]), async function(req, res) { const form = req[req.params.id]; await reviewLib.removeReviewRequest(req, res, form); @@ -564,6 +570,8 @@ module.exports = function(app) { '/forms/:id/review/results', auth.ensureAuthenticated, reqUtils.exist('id', Form), + // only available when under review + reqUtils.status('id', [0.5]), function(req, res, next) { const isReviewer = checkReviewer(req[req.params.id], req.session.userid); if (!isReviewer) { @@ -855,7 +863,7 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Form), reqUtils.canWriteMw('id'), - reqUtils.status('id', [0, 0.5]), + reqUtils.status('id', [0]), reqUtils.filter('body', ['html', 'title', 'description']), reqUtils.sanitize('body', ['html', 'title', 'description']), async function(req, res) { diff --git a/views/form-builder.jade b/views/form-builder.jade index dce6567d..51e1b810 100644 --- a/views/form-builder.jade +++ b/views/form-builder.jade @@ -61,7 +61,8 @@ block content .accordion-body.collapse(id="#{result._id}-comment") .accordion-inner span #{result.comment} - if (isReviewer) + //- reviewer and under review see the simple review form + if (isReviewer && status === 0.5) .accordion-group .accordion-heading a.accordion-toggle(data-toggle="collapse" data-parent="#reviews" href="#new-review") Add a new review @@ -87,7 +88,8 @@ block content form.form-horizontal#output != html .button-footer.container - if (status === 0 || status === 0.5) + //- edit controls available for draft + if (status === 0) .btn-group.dropup button.btn.dropdown-toggle(data-toggle='dropdown')#input-items Basic inputs span.caret @@ -124,22 +126,20 @@ block content button.btn.btn-primary#save Save .btn-group a.btn.btn-info#preview(data-toggle='tooltip', title='the form needs be saved first', href='preview', target='#{viewConfig.linkTarget}') Preview - .btn-group - button.btn.btn-primary#saveas(data-toggle='tooltip', title='create a copy of current form') Save as - if (status === 0 || status === 0.5 && !isReviewer) + if (status === 0) .btn-group - button.btn.btn-primary#numbering(data-toggle='tooltip', title='generate section numbers') Generate numbering + button.btn.btn-primary#saveas(data-toggle='tooltip', title='create a copy of current form') Save as + if (status === 0) .btn-group - button.btn.btn-warning#archive(data-toggle='tooltip', title='archive the form') Archive - if (status === 0 && !isReviewer) + button.btn.btn-primary#numbering(data-toggle='tooltip', title='generate section numbers') Generate numbering + .btn-group + button.btn.btn-warning#archive(data-toggle='tooltip', title='archive the form') Archive + if (status === 0) .btn-group button.btn.btn-primary#submit(data-toggle='tooltip', title='Submit for review') Submit for review if (status === 0.5 && !isReviewer && allApproved) .btn-group button.btn.btn-primary#release(data-toggle='tooltip', title='Release') Release - if (status === 1 && locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('admin') !== -1) - .btn-group - button.btn.btn-primary#obsolete(data-toggle='tooltip', title='Obsolete the form') Obsolete block js script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') From 1743d0363749056726d21243b1662f9471a33277 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Thu, 21 Apr 2022 19:47:36 -0700 Subject: [PATCH 07/58] fix binder share table (#109) --- views/binders.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/binders.jade b/views/binders.jade index 7ef1fbac..26132379 100644 --- a/views/binders.jade +++ b/views/binders.jade @@ -91,7 +91,7 @@ block content button.btn.btn-info.reload i.fa.fa-refresh.fa-lg |  Reload table - table#shared-binder-table.table.table-bordered.table-hover + table#shared-binder-table.table.table-bordered.table-hover if (viewConfig.shareGroups) #groupsharedbinders.tab-pane .btn-toolbar From 0e70a3c9d5869fe133ef658125b7eb6fde2b097e Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sat, 30 Jul 2022 09:57:42 -0700 Subject: [PATCH 08/58] Pdf 88 fix (#110) * no ssl now * update version * fix view side nav, release name, pdf in place * add button to view and pdf * add missing js * avoid all break * adjust page layout * add dedicated print view * recover view * switch view * use html2pdf 0.9.3 * fix select in two tables * fix load discrpancy * fix print load discrepancy, print from the window * fix data-livestamp * use abs date * shorter date with time zone * fix time, update print css * include help in validation msg * add help block in print view * default with help, checkbox with rightside label * all labels and help block before the value --- Dockerfile | 4 +- package-lock.json | 4 +- package.json | 6 +- public/javascripts/form-builder.js | 4 +- public/javascripts/table.js | 26 +- public/javascripts/traveler-print.js | 274 ++++++++++++++++ public/javascripts/traveler-viewer.js | 25 +- public/javascripts/util.js | 4 +- public/javascripts/validation.js | 50 +-- public/stylesheets/traveler-print.css | 18 ++ routes/traveler.js | 433 +++++++++++++------------- views/layout.jade | 2 +- views/traveler-print.jade | 123 ++++++++ views/traveler-viewer.jade | 17 + views/traveler.jade | 6 +- 15 files changed, 727 insertions(+), 269 deletions(-) create mode 100644 public/javascripts/traveler-print.js create mode 100644 public/stylesheets/traveler-print.css create mode 100644 views/traveler-print.jade diff --git a/Dockerfile b/Dockerfile index e0e6213a..63ea5855 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,8 @@ FROM node:14-alpine WORKDIR /app COPY package.json . COPY package-lock.json . -RUN apk update && \ - apk add openssl +# RUN apk update && \ +# apk add openssl # Add Tini, see https://github.com/krallin/tini for why RUN apk add --no-cache tini diff --git a/package-lock.json b/package-lock.json index 109fa3b0..893d7efd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "traveler-aps", - "version": "1.5.1", + "name": "traveler", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 99a8c318..02ac45e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "traveler-aps", - "version": "1.5.1", + "name": "traveler", + "version": "3.0.0", "private": true, "scripts": { "start": "node app" @@ -8,7 +8,7 @@ "repository": { "type": "git", "url": "https://github.com/AdvancedPhotonSource/traveler.git", - "release": "APS-1.5.1" + "release": "3.0.0" }, "dependencies": { "basic-auth": "1.0.0", diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index 7029160e..9c38dfd9 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1517,7 +1517,7 @@ function binding_events() { }); }, }); - selectMultiEvent(priorVersionsTable); + selectMultiEvent('#prior_versions'); filterEvent(); } @@ -1551,7 +1551,7 @@ function binding_events() { aaSorting: [[3, 'desc']], sDom: sDomPage, }); - selectOneEvent(discrepancyTable); + selectOneEvent('#discrepancy'); filterEvent(); } $('#modal .modal-footer').html( diff --git a/public/javascripts/table.js b/public/javascripts/table.js index 213a6cd2..323535bd 100644 --- a/public/javascripts/table.js +++ b/public/javascripts/table.js @@ -23,8 +23,8 @@ function selectEvent() { }); } -function selectMultiEvent(oTable) { - $(oTable.$('tbody')).on('click', 'input.select-row', function(e) { +function selectMultiEvent(table) { + $(`${table} tbody`).on('click', 'input.select-row', function(e) { const tr = $(e.target).closest('tr'); if ($(tr).hasClass('row-selected')) { $(tr).removeClass('row-selected'); @@ -34,18 +34,20 @@ function selectMultiEvent(oTable) { }); } -function selectOneEvent(oTable) { - $('tbody').on('click', 'input.select-row', function(e) { - const tr = $(e.target).closest('tr'); - if ($(tr).hasClass('row-selected')) { - $(tr).removeClass('row-selected'); - } else { - oTable.$('tr.row-selected').removeClass('row-selected'); - oTable - .$('input.select-row') +function selectOneEvent(table) { + $(`${table} tbody`).on('click', 'input.select-row', function(e) { + if ($(this).prop('checked')) { + $(`${table} tr.row-selected`).removeClass('row-selected'); + $(`${table} input.select-row`) .not(this) .prop('checked', false); - $(tr).addClass('row-selected'); + $(e.target) + .closest('tr') + .addClass('row-selected'); + } else { + $(e.target) + .closest('tr') + .removeClass('row-selected'); } }); } diff --git a/public/javascripts/traveler-print.js b/public/javascripts/traveler-print.js new file mode 100644 index 00000000..44f49d0e --- /dev/null +++ b/public/javascripts/traveler-print.js @@ -0,0 +1,274 @@ +/*global clearInterval: false, clearTimeout: false, document: false, event: false, frames: false, history: false, Image: false, location: false, name: false, navigator: false, Option: false, parent: false, screen: false, setInterval: false, setTimeout: false, window: false, XMLHttpRequest: false, FormData: false */ +/*global moment: false, Binder: false, prefix: false*/ + +// temparary solution for the dirty forms +function cleanForm() { + $('.control-group-buttons').remove(); +} + +function fileHistory(found) { + var i; + var output = ''; + var link; + if (found.length > 0) { + for (i = 0; i < found.length; i += 1) { + link = prefix + '/data/' + found[i]._id; + output = + output + + '' + + found[i].value + + ' uploaded by ' + + found[i].inputBy + + ' ' + + livespan(found[i].inputOn, false) + + '; '; + } + } + return output; +} + +function notes(found) { + var i; + var output = '
    '; + if (found.length > 0) { + for (i = 0; i < found.length; i += 1) { + output = + output + + '
    ' + + found[i].inputBy + + ' noted ' + + livespan(found[i].inputOn, false) + + ':
    '; + output = output + '
    ' + found[i].value + '
    '; + } + } + return output + '
    '; +} + +function renderNotes() { + $.ajax({ + url: './notes/', + type: 'GET', + dataType: 'json', + }) + .done(function(data) { + $('#form .controls').each(function(index, controlsElement) { + var inputElements = $(controlsElement).find('input,textarea'); + if (inputElements.length) { + var element = inputElements[0]; + var found = data.filter(function(e) { + return e.name === element.name; + }); + $(element) + .closest('.controls') + .append( + '' + ); + if (found.length) { + found.sort(function(a, b) { + if (a.inputOn > b.inputOn) { + return -1; + } + return 1; + }); + $(element) + .closest('.controls') + .append( + '' + ); + } + } + }); + }) + .fail(function(jqXHR) { + if (jqXHR.status !== 401) { + $('#message').append( + '
    Cannot get saved traveler data
    ' + ); + $(window).scrollTop($('#message div:last-child').offset().top - 40); + } + }) + .always(); +} + +function loadDiscrepancyLog(discrepancyForm) { + DiscrepancyFormLoader.setForm(discrepancyForm); + DiscrepancyFormLoader.renderLogs(); + DiscrepancyFormLoader.retrieveLogs(); +} + +$(function() { + ajax401(prefix); + + // createSideNav(); + + cleanForm(); + + // load discrepancy table and data + var discrepancyForm; + if (traveler.activeDiscrepancyForm) { + discrepancyForm = findById( + traveler.discrepancyForms, + traveler.activeDiscrepancyForm + ); + DiscrepancyFormLoader.setLogTable('#discrepancy-log-table'); + DiscrepancyFormLoader.setTid(traveler._id); + loadDiscrepancyLog(discrepancyForm); + } + + $('span.time').each(function() { + $(this).text( + moment($(this).text()).format('dddd, MMMM Do YYYY, h:mm:ss a') + ); + }); + + $('#location').attr('href', window.location); + // .text(window.location); + $('#localtime').text(Date()); + + // update img + $('#form') + .find('img') + .each(function() { + var $this = $(this); + if ($this.attr('name')) { + if ($this.attr('src') === undefined) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + if ($this.attr('src').indexOf('http') === 0) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + if (prefix && $this.attr('src').indexOf(prefix) !== 0) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + } + }); + + var binder = new Binder.FormBinder(document.forms[0]); + $.ajax({ + url: './data/', + type: 'GET', + dataType: 'json', + }) + .done(function(data) { + $('#form .controls').each(function(index, controlsElement) { + var inputElements = $(controlsElement).find('input,textarea'); + if (inputElements.length) { + var element = inputElements[0]; + var found = data.filter(function(e) { + return e.name === element.name; + }); + if (found.length) { + found.sort(function(a, b) { + if (a.inputOn > b.inputOn) { + return -1; + } + return 1; + }); + if (element.type === 'file') { + $(element) + .closest('.controls') + .append( + '
    history: ' + + fileHistory(found) + + '
    ' + ); + } else { + var currentValue = found[0].value; + if (found[0].inputType === 'radio') { + // Update element to match the value + for (var i = 0; i < inputElements.size(); i++) { + var ittrInput = inputElements[i]; + if (ittrInput.value === currentValue) { + element = ittrInput; + break; + } + } + } + binder.deserializeFieldFromValue(element, currentValue); + binder.accessor.set(element.name, currentValue); + $(element) + .closest('.controls') + .append( + '
    history: ' + + history(found) + + '
    ' + ); + } + } + } + }); + + markFormValidity(document.getElementById('form')); + + // load the notes here + renderNotes(); + }) + .fail(function() { + $('#message').append( + '
    Cannot get saved traveler data
    ' + ); + $(window).scrollTop($('#message div:last-child').offset().top - 40); + }) + .always(); + + $('#form').on('click', 'a.notes-number', function(e) { + e.preventDefault(); + var $input_notes = $(this) + .closest('.controls') + .find('.input-notes'); + if ($input_notes.is(':visible')) { + $input_notes.hide(); + } else { + $input_notes.show(); + } + }); + + $('#show-validation').click(function() { + $('.validation').remove(); + $('#validation').html( + '

    Summary

    ' + validationMessage(document.getElementById('form')) + ); + $('#validation').show(); + }); + + $('#hide-validation').click(function() { + $('#validation').hide(); + $('.validation').hide(); + }); + + $('#show-notes').click(function() { + $('.input-notes').show(); + }); + + $('#hide-notes').click(function() { + $('.input-notes').hide(); + }); + + $('#show-details').click(function() { + $('#details').show(); + }); + + $('#hide-details').click(function() { + $('#details').hide(); + }); + + $('#create-pdf').click(function() { + window.onbeforeprint = event => { + $('#print-control').hide(); + }; + window.onafterprint = event => { + $('#print-control').show(); + }; + window.print(); + }); +}); diff --git a/public/javascripts/traveler-viewer.js b/public/javascripts/traveler-viewer.js index ef5c1d25..2590c05a 100644 --- a/public/javascripts/traveler-viewer.js +++ b/public/javascripts/traveler-viewer.js @@ -24,7 +24,7 @@ function fileHistory(found) { ' uploaded by ' + found[i].inputBy + ' ' + - livespan(found[i].inputOn) + + livespan(found[i].inputOn, false) + '; '; } } @@ -41,7 +41,7 @@ function notes(found) { '
    ' + found[i].inputBy + ' noted ' + - livespan(found[i].inputOn) + + livespan(found[i].inputOn, false) + ':
    '; output = output + '
    ' + found[i].value + '
    '; } @@ -99,10 +99,31 @@ function renderNotes() { .always(); } +function loadDiscrepancyLog(discrepancyForm) { + DiscrepancyFormLoader.setForm(discrepancyForm); + DiscrepancyFormLoader.renderLogs(); + DiscrepancyFormLoader.retrieveLogs(); +} + $(function() { + ajax401(prefix); + createSideNav(); + cleanForm(); + // load discrepancy table and data + var discrepancyForm; + if (traveler.activeDiscrepancyForm) { + discrepancyForm = findById( + traveler.discrepancyForms, + traveler.activeDiscrepancyForm + ); + DiscrepancyFormLoader.setLogTable('#discrepancy-log-table'); + DiscrepancyFormLoader.setTid(traveler._id); + loadDiscrepancyLog(discrepancyForm); + } + $('span.time').each(function() { $(this).text( moment($(this).text()).format('dddd, MMMM Do YYYY, h:mm:ss a') diff --git a/public/javascripts/util.js b/public/javascripts/util.js index 59b67a85..48f47370 100644 --- a/public/javascripts/util.js +++ b/public/javascripts/util.js @@ -97,9 +97,7 @@ function livespan(stamp, live = true) { return ''; } else { return ( - '' + - moment(stamp).format('dddd, MMMM Do YYYY, h:mm:ss a') + - '' + '' + moment(stamp).format('MMM D YYYY, HH:mm:ss ZZ') + '' ); } } diff --git a/public/javascripts/validation.js b/public/javascripts/validation.js index f4382784..163ad197 100644 --- a/public/javascripts/validation.js +++ b/public/javascripts/validation.js @@ -60,19 +60,17 @@ function validationMessage(form) { if (disabled) { $('input,textarea', form).prop('disabled', false); } - var i = 0; - var output = $('
    '); - var p; - var value; - var input; - var $input; - var label; - var span; - var last_input_name = ''; - for (i = 0; i < form.elements.length; i += 1) { - input = form.elements[i]; - - var input_name = input.name; + let output = $('
    '); + let last_input_name = ''; + for (let i = 0; i < form.elements.length; i += 1) { + let p; + let value; + let help; + let input = form.elements[i]; + let $input; + let span; + let label = []; + let input_name = input.name; if (input_name === last_input_name) { continue; } @@ -121,15 +119,25 @@ function validationMessage(form) { } else { value = input.value; } - label = $(input) - .closest('.control-group') - .children('.control-label') - .text(); - if (label === '' && input.type === 'checkbox') { - label = $(input) - .next() - .text(); + label.push( + $(input) + .closest('.control-group') + .children('.control-label') + .text() + ); + if (input.type === 'checkbox') { + label.push( + $(input) + .next() + .text() + ); } + help = $(input) + .closest('.controls') + .children('.help-block') + .text(); + label.push(help); + label = label.filter(e => e.length > 0).join(' '); if (customCheckValidity(input)) { p.html('' + label + ': ' + value); span.text('validation passed'); diff --git a/public/stylesheets/traveler-print.css b/public/stylesheets/traveler-print.css new file mode 100644 index 00000000..a0c84d7a --- /dev/null +++ b/public/stylesheets/traveler-print.css @@ -0,0 +1,18 @@ +body { + font: 12pt Georgia, 'Times New Roman', Times, serif; + line-height: 1.2; + background: #fff !important; + color: #000; +} + +.control-group-wrap { + page-break-inside: avoid; +} + +.fe-type { + display: none; +} + +.new-note { + display: none; +} diff --git a/routes/traveler.js b/routes/traveler.js index c576079e..e95f65e2 100644 --- a/routes/traveler.js +++ b/routes/traveler.js @@ -1,32 +1,33 @@ -/*eslint max-nested-callbacks: [2, 4], complexity: [2, 20]*/ -var config = require('../config/config.js'); - -var routesUtilities = require('../utilities/routes.js'); -const mqttUtilities = require('../utilities/mqtt.js'); - -var fs = require('fs'); -var auth = require('../lib/auth'); -var authConfig = config.auth; -var mongoose = require('mongoose'); -var path = require('path'); -var _ = require('lodash'); -var reqUtils = require('../lib/req-utils'); -var shareLib = require('../lib/share'); -var tag = require('../lib/tag'); -var DataError = require('../lib/error').DataError; - -var ReleasedForm = mongoose.model('ReleasedForm'); -var User = mongoose.model('User'); -var Group = mongoose.model('Group'); -var Traveler = mongoose.model('Traveler'); -var TravelerData = mongoose.model('TravelerData'); -var TravelerNote = mongoose.model('TravelerNote'); -var Log = mongoose.model('Log'); - -var TravelerError = require('../lib/error').TravelerError; - -var debug = require('debug')('traveler:route:traveler'); -var logger = require('../lib/loggers').getLogger(); +/* eslint max-nested-callbacks: [2, 4], complexity: [2, 20] */ +const fs = require('fs'); +const path = require('path'); +const debug = require('debug')('traveler:route:traveler'); +const mongoose = require('mongoose'); +const _ = require('lodash'); + +const config = require('../config/config'); +const routesUtilities = require('../utilities/routes'); +const mqttUtilities = require('../utilities/mqtt'); + +const auth = require('../lib/auth'); + +const authConfig = config.auth; +const reqUtils = require('../lib/req-utils'); +const shareLib = require('../lib/share'); +const tag = require('../lib/tag'); +const { DataError } = require('../lib/error'); + +const ReleasedForm = mongoose.model('ReleasedForm'); +const User = mongoose.model('User'); +const Group = mongoose.model('Group'); +const Traveler = mongoose.model('Traveler'); +const TravelerData = mongoose.model('TravelerData'); +const TravelerNote = mongoose.model('TravelerNote'); +const Log = mongoose.model('Log'); + +const { TravelerError } = require('../lib/error'); +const { stateTransition } = require('../model/traveler'); +const logger = require('../lib/loggers').getLogger(); function addInputName(name, list) { if (list.indexOf(name) === -1) { @@ -48,11 +49,11 @@ function resetTouched(doc, cb) { return cb(dataErr); } // reset the touched input name list and the finished input number - logger.info('reset the touched inputs for traveler ' + doc._id); - var labels = {}; - var activeForm; + logger.info(`reset the touched inputs for traveler ${doc._id}`); + let labels = {}; + let activeForm; if (doc.forms.length === 1) { - activeForm = doc.forms[0]; + [activeForm] = doc.forms; } else { activeForm = doc.forms.id(doc.activeForm); } @@ -71,7 +72,7 @@ function resetTouched(doc, cb) { }); // finished input doc.finishedInput = doc.touchedInputs.length; - cb(); + return cb(); }); } @@ -89,27 +90,23 @@ function createTraveler(form, req, res) { } return res.status(500).send(err.message); } - logger.info('new traveler ' + doc.id + ' created'); - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/travelers/' + - doc.id + - '/'; + logger.info(`new traveler ${doc.id} created`); + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/travelers/${doc.id}/`; res.set('Location', url); return res.status(201).json({ - location: - (req.proxied ? req.proxied_prefix : '') + - '/travelers/' + - doc.id + - '/', + location: `${req.proxied ? req.proxied_prefix : ''}/travelers/${ + doc.id + }/`, }); } ); } function cloneTraveler(source, req, res) { - var traveler = new Traveler({ - title: source.title + ' clone', + const traveler = new Traveler({ + title: `${source.title} clone`, description: source.description, devices: [], tags: source.tags, @@ -135,7 +132,7 @@ function cloneTraveler(source, req, res) { console.error(err); return res.status(500).send(err.message); } - logger.info('new traveler ' + doc.id + ' created'); + logger.info(`new traveler ${doc.id} created`); doc.sharedWith.forEach(function(e) { User.findByIdAndUpdate( e._id, @@ -149,7 +146,7 @@ function cloneTraveler(source, req, res) { logger.error(userErr); } if (!user) { - logger.error('The user ' + e._id + ' does not in the db'); + logger.error(`The user ${e._id} does not in the db`); } } ); @@ -168,17 +165,15 @@ function cloneTraveler(source, req, res) { logger.error(groupErr); } if (!user) { - logger.error('The group ' + e._id + ' does not in the db'); + logger.error(`The group ${e._id} does not in the db`); } } ); }); - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/travelers/' + - doc.id + - '/'; + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/travelers/${doc.id}/`; res.set('Location', url); return res.status(201).json({ location: url, @@ -236,7 +231,7 @@ module.exports = function(app) { logger.error(err); return res.status(500).send(err.message); } - res.status(200).json(travelers); + return res.status(200).json(travelers); }); }); @@ -257,7 +252,7 @@ module.exports = function(app) { if (!me) { return res.status(400).send('cannot identify the current user'); } - Traveler.find( + return Traveler.find( { _id: { $in: me.travelers, @@ -295,9 +290,9 @@ module.exports = function(app) { logger.error(err); return res.status(500).send(err.message); } - var travelerIds = []; - var i; - var j; + const travelerIds = []; + let i; + let j; // merge the travelers arrays for (i = 0; i < groups.length; i += 1) { for (j = 0; j < groups[i].travelers.length; j += 1) { @@ -306,7 +301,7 @@ module.exports = function(app) { } } } - Traveler.find( + return Traveler.find( { _id: { $in: travelerIds, @@ -320,7 +315,7 @@ module.exports = function(app) { logger.error(tErr); return res.status(500).send(tErr.message); } - res.status(200).json(travelers); + return res.status(200).json(travelers); }); }); }); @@ -345,7 +340,7 @@ module.exports = function(app) { logger.error(err); return res.status(500).send(err.message); } - res.status(200).json(travelers); + return res.status(200).json(travelers); }); }); @@ -384,13 +379,13 @@ module.exports = function(app) { return res.render('currenttravelers', { device: req.query.device || null }); - });*/ + }); */ app.get('/archivedtravelers/json', auth.ensureAuthenticated, function( req, res ) { - var search = { + const search = { $and: [ { $or: [ @@ -444,16 +439,13 @@ module.exports = function(app) { return res.status(500).send(err.message); } if (form) { - createTraveler(form, req, res); - } else { - return res - .status(400) - .send( - `cannot find the released ${ - config.viewConfig.terminology.form - } with id ${req.body.form}` - ); + return createTraveler(form, req, res); } + return res + .status(400) + .send( + `cannot find the released ${config.viewConfig.terminology.form} with id ${req.body.form}` + ); }); } if (req.body.source) { @@ -463,21 +455,16 @@ module.exports = function(app) { return res.status(500).send(err.message); } if (traveler) { - // if (traveler.status === 0) { - // return res.status(400).send('You cannot clone an initialized traveler.'); - // } if (reqUtils.canRead(req, traveler)) { - cloneTraveler(traveler, req, res); - } else { - return res - .status(400) - .send('You cannot clone a traveler that you cannot read.'); + return cloneTraveler(traveler, req, res); } - } else { return res .status(400) - .send('cannot find the traveler ' + req.body.source); + .send('You cannot clone a traveler that you cannot read.'); } + return res + .status(400) + .send(`cannot find the traveler ${req.body.source}`); }); } } @@ -488,43 +475,45 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Traveler), function getTraveler(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; if (doc.archived) { return res.redirect( - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/travelers/' + - req.params.id + - '/view' + `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/travelers/${req.params.id}/view` ); } if (reqUtils.canWrite(req, doc)) { - routesUtilities.getDeviceValue(doc.devices).then(function(value) { - doc.devices = value; - return res.render( - 'traveler', - routesUtilities.getRenderObject(req, { - isOwner: reqUtils.isOwner(req, doc), - traveler: doc, - formHTML: - doc.forms.length === 1 - ? doc.forms[0].html - : doc.forms.id(doc.activeForm).html, - }) - ); - }); - } else if (reqUtils.canRead(req, doc)) { + return routesUtilities + .getDeviceValue(doc.devices) + .then(function(value) { + doc.devices = value; + return res.render( + 'traveler', + routesUtilities.getRenderObject(req, { + isOwner: reqUtils.isOwner(req, doc), + traveler: doc, + formHTML: + doc.forms.length === 1 + ? doc.forms[0].html + : doc.forms.id(doc.activeForm).html, + }) + ); + }); + } + + if (reqUtils.canRead(req, doc)) { return res.redirect( - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/travelers/' + - req.params.id + - '/view' + `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/travelers/${req.params.id}/view` ); - } else { - return res - .status(403) - .send('You are not authorized to access this resource'); } + + return res + .status(403) + .send('You are not authorized to access this resource'); } ); @@ -533,7 +522,7 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', Traveler), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; routesUtilities.getDeviceValue(doc.devices).then(function(value) { res.devices = value; return res.render( @@ -546,6 +535,24 @@ module.exports = function(app) { } ); + app.get( + '/travelers/:id/print', + auth.ensureAuthenticated, + reqUtils.exist('id', Traveler), + function(req, res) { + const doc = req[req.params.id]; + routesUtilities.getDeviceValue(doc.devices).then(function(value) { + res.devices = value; + return res.render( + 'traveler-print', + routesUtilities.getRenderObject(req, { + traveler: req[req.params.id], + }) + ); + }); + } + ); + app.get( '/travelers/:id/json', auth.ensureAuthenticated, @@ -582,7 +589,7 @@ module.exports = function(app) { return null; } - var found = data.filter(function(d) { + const found = data.filter(function(d) { return d.name === name; }); // get the latest value from history @@ -607,11 +614,11 @@ module.exports = function(app) { * @return {Object} the json representation */ function retrieveKeyvalue(traveler, props, cb) { - var output = {}; + const output = {}; props.forEach(function(p) { output[p] = traveler[p]; }); - var mapping = traveler.mapping; + const { mapping } = traveler; TravelerData.find( { _id: { @@ -623,7 +630,7 @@ module.exports = function(app) { if (dataErr) { return cb(dataErr); } - var userDefined = {}; + const userDefined = {}; _.mapKeys(mapping, function(name, key) { userDefined[key] = dataForName(name, docs); }); @@ -641,14 +648,14 @@ module.exports = function(app) { * @return {Object} the json representation */ function retrieveKeyLableValue(traveler, props, cb) { - var output = {}; + const output = {}; props.forEach(function(p) { output[p] = traveler[p]; }); - var mapping = traveler.mapping; - var labels = traveler.labels; - var discrepancyMapping = {}; - var discrepancyLabels = {}; + const { mapping } = traveler; + const { labels } = traveler; + let discrepancyMapping = {}; + let discrepancyLabels = {}; if (traveler.activeDiscrepancyForm) { discrepancyMapping = traveler.discrepancyForms.id( traveler.activeDiscrepancyForm @@ -668,7 +675,7 @@ module.exports = function(app) { if (dataErr) { return cb(dataErr); } - var userDefined = {}; + const userDefined = {}; _.mapKeys(mapping, function(name, key) { userDefined[key] = {}; userDefined[key].value = dataForName(name, docs); @@ -676,7 +683,7 @@ module.exports = function(app) { userDefined[key].label = labels[name]; } }); - var discrepancy = {}; + const discrepancy = {}; _.mapKeys(discrepancyMapping, function(name, key) { discrepancy[key] = {}; discrepancy[key].value = dataForName(name, docs); @@ -696,7 +703,7 @@ module.exports = function(app) { } // retrieve all log data in one find - Log.find( + return Log.find( { _id: { $in: traveler.discrepancyLogs, @@ -781,19 +788,19 @@ module.exports = function(app) { reqUtils.hasAll('body', ['form']), reqUtils.sanitize('body', ['form']), function(req, res) { - var traveler = req[req.params.id]; + const traveler = req[req.params.id]; // check active discrepancy form if (req.body.form !== traveler.referenceDiscrepancyForm.toString()) { return res.status(400).send('Discrepancy form does not match.'); } // create log - var log = new Log({ + const log = new Log({ referenceForm: traveler.referenceDiscrepancyForm, data: [], }); // save log, and add reference to traveler - var newLog; - log + let newLog; + return log .save() .then(function(doc) { newLog = doc; @@ -805,7 +812,7 @@ module.exports = function(app) { }) .catch(err => { logger.error(err); - res.status(500).send(err.message); + return res.status(500).send(err.message); }); } ); @@ -819,16 +826,16 @@ module.exports = function(app) { reqUtils.exist('lid', Log), reqUtils.sanitize('body'), function(req, res) { - var log = req[req.params.lid]; + const log = req[req.params.lid]; log.records = []; - _.keys(req.body).map(function(name) { - log.records.push({ name: name, value: req.body[name] }); + _.keys(req.body).forEach(function(name) { + log.records.push({ name, value: req.body[name] }); }); if (req.files) { - _.keys(req.files).map(function(name) { - let file = req.files[name]; + _.keys(req.files).forEach(function(name) { + const file = req.files[name]; log.records.push({ - name: name, + name, value: file.originalname, file: { path: file.path, @@ -841,7 +848,7 @@ module.exports = function(app) { log.inputBy = req.session.userid; log.inputOn = Date.now(); debug(log); - let travelerId = req.params.id + const travelerId = req.params.id; mqttUtilities.postDiscrepancyLogAddedMessage(travelerId, log); log .save() @@ -876,7 +883,7 @@ module.exports = function(app) { if (!record.file.path) { return res.status(200).json(record); } - fs.exists(record.file.path, function(exists) { + return fs.exists(record.file.path, function(exists) { if (exists) { return res.sendFile(path.resolve(record.file.path)); } @@ -893,7 +900,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.filter('body', ['archived']), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; if (doc.archived === req.body.archived) { return res.status(204).send(); } @@ -904,7 +911,7 @@ module.exports = function(app) { doc.archivedOn = Date.now(); } - doc.save(function(saveErr, newDoc) { + return doc.save(function(saveErr, newDoc) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); @@ -912,10 +919,7 @@ module.exports = function(app) { return res .status(200) .send( - 'traveler ' + - req.params.id + - ' archived state set to ' + - newDoc.archived + `traveler ${req.params.id} archived state set to ${newDoc.archived}` ); }); } @@ -929,7 +933,7 @@ module.exports = function(app) { reqUtils.status('id', [0, 1, 1.5]), reqUtils.filter('body', ['name']), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; shareLib.changeOwner(req, res, doc); } ); @@ -940,8 +944,11 @@ module.exports = function(app) { reqUtils.exist('id', Traveler), reqUtils.archived('id', false), function(req, res) { - var doc = req[req.params.id]; - if (reqUtils.isOwner(req, doc) || routesUtilities.checkUserRole(req, 'admin')) { + const doc = req[req.params.id]; + if ( + reqUtils.isOwner(req, doc) || + routesUtilities.checkUserRole(req, 'admin') + ) { return res.render( 'traveler-config', routesUtilities.getRenderObject(req, { @@ -949,11 +956,10 @@ module.exports = function(app) { isOwner: reqUtils.isOwner(req, doc), }) ); - } else { - res - .status(403) - .send('you are not authorized to access this resource'); } + return res + .status(403) + .send('you are not authorized to access this resource'); } ); @@ -966,27 +972,25 @@ module.exports = function(app) { reqUtils.filter('body', ['title', 'description', 'deadline']), reqUtils.sanitize('body', ['title', 'description', 'deadline']), function(req, res) { - var doc = req[req.params.id]; - if (reqUtils.isOwner(req, doc) || routesUtilities.checkUserRole(req, 'admin')) { - var k; - for (k in req.body) { - if (req.body.hasOwnProperty(k) && req.body[k] !== null) { - doc[k] = req.body[k]; - } - } + const doc = req[req.params.id]; + if ( + reqUtils.isOwner(req, doc) || + routesUtilities.checkUserRole(req, 'admin') + ) { + Object.keys(req.body).forEach(k => { + doc[k] = req.body[k]; + }); doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); - doc.save(function (saveErr, newDoc) { + doc.save(function(saveErr, newDoc) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); } - var out = {}; - for (k in req.body) { - if (req.body.hasOwnProperty(k) && req.body[k] !== null) { - out[k] = newDoc.get(k); - } - } + const out = {}; + Object.keys(req.body).forEach(k => { + out[k] = newDoc.get(k); + }); return res.status(200).json(out); }); } else { @@ -1015,7 +1019,7 @@ module.exports = function(app) { reqUtils.canWriteMw('id'), reqUtils.archived('id', false), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; if ([1, 1.5, 2, 3, 4].indexOf(req.body.status) === -1) { return res.status(400).send('invalid status'); @@ -1025,9 +1029,7 @@ module.exports = function(app) { return res.status(204).send(); } - var stateTransition = require('../model/traveler').stateTransition; - - var target = _.find(stateTransition, function(t) { + const target = _.find(stateTransition, function(t) { return t.from === doc.status; }); @@ -1051,11 +1053,8 @@ module.exports = function(app) { } if ( - ( - (doc.status === 2 && req.body.status === 4) || - (doc.status === 2 && req.body.status === 1) - ) - && + ((doc.status === 2 && req.body.status === 4) || + (doc.status === 2 && req.body.status === 1)) && !( routesUtilities.checkUserRole(req, 'admin') || routesUtilities.checkUserRole(req, 'manager') @@ -1070,12 +1069,12 @@ module.exports = function(app) { doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); mqttUtilities.postTravelerStatusChangedMessage(doc); - doc.save(function(saveErr) { + return doc.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); } - return res.status(200).send('status updated to ' + req.body.status); + return res.status(200).send(`status updated to ${req.body.status}`); }); } ); @@ -1094,18 +1093,18 @@ module.exports = function(app) { reqUtils.filter('body', ['newdevice']), reqUtils.sanitize('body', ['newdevice']), function(req, res) { - var newdevice = req.body.newdevice; + const { newdevice } = req.body; if (!newdevice) { return res.status(400).send('the new device name not accepted'); } - var doc = req[req.params.id]; + const doc = req[req.params.id]; doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); - var added = doc.devices.addToSet(newdevice); + const added = doc.devices.addToSet(newdevice); if (added.length === 0) { return res.status(204).send(); } - doc.save(function(saveErr) { + return doc.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); @@ -1125,7 +1124,7 @@ module.exports = function(app) { reqUtils.archived('id', false), reqUtils.status('id', [0, 1]), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); doc.devices.pull(req.params.number); @@ -1145,7 +1144,7 @@ module.exports = function(app) { reqUtils.exist('id', Traveler), reqUtils.canReadMw('id'), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; TravelerData.find( { _id: { @@ -1174,8 +1173,8 @@ module.exports = function(app) { reqUtils.hasAll('body', ['name', 'value', 'type']), reqUtils.sanitize('body', ['name', 'value', 'type']), function(req, res) { - var doc = req[req.params.id]; - var data = new TravelerData({ + const doc = req[req.params.id]; + const data = new TravelerData({ traveler: doc._id, name: req.body.name, value: req.body.value, @@ -1200,7 +1199,7 @@ module.exports = function(app) { mqttUtilities.postTravelerDataChangedMessage(data); doc.data.push(data._id); // update the finishe input number by reset - resetTouched(doc, function() { + return resetTouched(doc, function() { // save doc anyway doc.save(function(saveErr) { if (saveErr) { @@ -1220,7 +1219,7 @@ module.exports = function(app) { reqUtils.exist('id', Traveler), reqUtils.canReadMw('id'), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; TravelerNote.find( { _id: { @@ -1248,8 +1247,8 @@ module.exports = function(app) { reqUtils.hasAll('body', ['name', 'value']), reqUtils.sanitize('body', ['name', 'value']), function(req, res) { - var doc = req[req.params.id]; - var note = new TravelerNote({ + const doc = req[req.params.id]; + const note = new TravelerNote({ traveler: doc._id, name: req.body.name, value: req.body.value, @@ -1268,7 +1267,7 @@ module.exports = function(app) { }); doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); - doc.save(function(saveErr) { + return doc.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); @@ -1286,13 +1285,13 @@ module.exports = function(app) { reqUtils.canWriteMw('id'), reqUtils.status('id', [1]), function(req, res) { - var doc = req[req.params.id]; + const doc = req[req.params.id]; if (_.isEmpty(req.files)) { return res.status(400).send('Expect One uploaded file'); } - var data = new TravelerData({ + const data = new TravelerData({ traveler: doc._id, name: req.body.name, value: req.files[req.body.name].originalname, @@ -1306,7 +1305,7 @@ module.exports = function(app) { inputOn: Date.now(), }); - data.save(function(dataErr) { + return data.save(function(dataErr) { if (dataErr) { logger.error(dataErr); return res.status(500).send(dataErr.message); @@ -1314,15 +1313,14 @@ module.exports = function(app) { doc.data.push(data._id); doc.updatedBy = req.session.userid; doc.updatedOn = Date.now(); - doc.save(function(saveErr) { + return doc.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); } - var url = - (req.proxied ? authConfig.proxied_service : authConfig.service) + - '/data/' + - data._id; + const url = `${ + req.proxied ? authConfig.proxied_service : authConfig.service + }/data/${data._id}`; res.set('Location', url); return res.status(201).json({ location: url, @@ -1337,7 +1335,7 @@ module.exports = function(app) { auth.ensureAuthenticated, reqUtils.exist('id', TravelerData), function(req, res) { - var data = req[req.params.id]; + const data = req[req.params.id]; if (data.inputType === 'file') { fs.exists(data.file.path, function(exists) { if (exists) { @@ -1358,7 +1356,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.archived('id', false), function(req, res) { - var traveler = req[req.params.id]; + const traveler = req[req.params.id]; return res.render( 'share', routesUtilities.getRenderObject(req, { @@ -1380,9 +1378,9 @@ module.exports = function(app) { reqUtils.archived('id', false), reqUtils.filter('body', ['access']), function(req, res) { - var traveler = req[req.params.id]; + const traveler = req[req.params.id]; // change the access - var access = req.body.access; + let { access } = req.body; if (['-1', '0', '1'].indexOf(access) === -1) { return res.status(400).send('not valid value'); } @@ -1391,14 +1389,14 @@ module.exports = function(app) { return res.status(204).send(); } traveler.publicAccess = access; - traveler.save(function(saveErr) { + return traveler.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); } return res .status(200) - .send('public access is set to ' + req.body.access); + .send(`public access is set to ${req.body.access}`); }); } ); @@ -1409,7 +1407,7 @@ module.exports = function(app) { reqUtils.exist('id', Traveler), reqUtils.isOwnerMw('id'), function(req, res) { - var traveler = req[req.params.id]; + const traveler = req[req.params.id]; if (req.params.list === 'users') { return res.status(200).json(traveler.sharedWith || []); } @@ -1427,8 +1425,8 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.archived('id', false), function(req, res) { - var traveler = req[req.params.id]; - var share = -2; + const traveler = req[req.params.id]; + let share = -2; if (req.params.list === 'users') { if (req.body.name) { share = reqUtils.getSharedWith(traveler.sharedWith, req.body.name); @@ -1453,14 +1451,11 @@ module.exports = function(app) { .status(400) .send( req.body.name || - req.body.id + ' is already in the ' + req.params.list + ' list.' + `${req.body.id} is already in the ${req.params.list} list.` ); } - if (share === -1) { - // new user - shareLib.addShare(req, res, traveler); - } + return shareLib.addShare(req, res, traveler); } ); @@ -1471,8 +1466,8 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.archived('id', false), function(req, res) { - var traveler = req[req.params.id]; - var share; + const traveler = req[req.params.id]; + let share; if (req.params.list === 'users') { share = traveler.sharedWith.id(req.params.shareid); } @@ -1482,7 +1477,7 @@ module.exports = function(app) { if (!share) { return res .status(400) - .send('cannot find ' + req.params.shareid + ' in the list.'); + .send(`cannot find ${req.params.shareid} in the list.`); } // change the access if (req.body.access && req.body.access === 'write') { @@ -1490,13 +1485,13 @@ module.exports = function(app) { } else { share.access = 0; } - traveler.save(function(saveErr) { + return traveler.save(function(saveErr) { if (saveErr) { logger.error(saveErr); return res.status(500).send(saveErr.message); } // check consistency of user's traveler list - var Target; + let Target; if (req.params.list === 'users') { Target = User; } @@ -1516,7 +1511,7 @@ module.exports = function(app) { } if (!target) { logger.error( - 'The user/group ' + req.params.userid + ' is not in the db' + `The user/group ${req.params.userid} is not in the db` ); } } @@ -1533,7 +1528,7 @@ module.exports = function(app) { reqUtils.isOwnerMw('id'), reqUtils.archived('id', false), function(req, res) { - var traveler = req[req.params.id]; + const traveler = req[req.params.id]; shareLib.removeShare(req, res, traveler); } ); diff --git a/views/layout.jade b/views/layout.jade index ae26da2b..fc31a599 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -68,7 +68,7 @@ html block content .container-fluid h6.text-right - a(href='https://github.com/AdvancedPhotonSource/traveler/releases/tag/#{viewConfig.appVersion}', target='_blank') Release #{viewConfig.appVersion} + a(href='https://github.com/dongliu/traveler', target='_blank') Release #{viewConfig.appVersion} script(type='text/javascript', src='#{prefix}/dependencies/holder.js') block js diff --git a/views/traveler-print.jade b/views/traveler-print.jade new file mode 100644 index 00000000..89ec608a --- /dev/null +++ b/views/traveler-print.jade @@ -0,0 +1,123 @@ +doctype html +html + block head + meta(charset='urf-8') + title #{orgName}_traveler_#{traveler._id} + link(rel='stylesheet', href='#{prefix}/bootstrap/css/bootstrap.css') + link(rel='stylesheet', href='#{prefix}/font-awesome-4.3.0/css/font-awesome.css') + link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') + link(rel='stylesheet', href='#{prefix}/stylesheets/traveler-print.css') + + block content + script(type='text/javascript'). + var traveler = !{JSON.stringify(traveler)}; + var prefix = '!{prefix}'; + var title = '!{traveler.title}' + .container + .row + .span10#print-control + br + if (locals.traveler.status !== undefined && locals.traveler.status === 2) + .btn-group + button.btn.btn-primary#create-pdf Create PDF + .btn-group(data-toggle='buttons-radio') + button.btn.btn-info#show-validation Show validation + button.btn.btn-info#hide-validation.active Hide validation + .btn-group(data-toggle='buttons-radio') + button.btn.btn-info#show-notes Show notes + button.btn.btn-info#hide-notes.active Hide notes + .btn-group(data-toggle='buttons-radio') + button.btn.btn-info#show-details Show details + button.btn.btn-info#hide-details.active Hide details + .span10#traveler-print + h3 Traveler title: #{traveler.title} + if (locals.traveler.status !== undefined) + h4 Status: + case locals.traveler.status + when 1 + = ' ' + span active + when 1.5 + = ' ' + span submitted + when 2 + = ' ' + span completed + when 3 + = ' ' + span frozen + when 0 + = ' ' + span initialized + default + = ' ' + if (locals.traveler.status > 0) + = '; ' + span + em#finished-input #{traveler.finishedInput} + = ' inputs finished out of ' + span + em #{traveler.totalInput} + = ' inputs' + if (locals.traveler.devices && traveler.devices.length > 0) + h4 Tags: !{traveler.devices} + #validation.well.hide + #details.hide + if (locals.traveler.description !== undefined) + h4 Description: #{traveler.description} + if (locals.traveler.deadline !== undefined) + p Deadline: + = ' ' + span.time #{traveler.deadline} + p Created by #{traveler.createdBy} + = ' ' + if (locals.traveler.clonedBy !== undefined) + | cloned by #{traveler.clonedBy} on + else + | on + = ' ' + span.time #{traveler.createdOn} + if (locals.traveler.updatedBy !== undefined) + p Updated by #{traveler.updatedBy} on + = ' ' + span.time #{traveler.updatedOn} + br + if locals.traveler.activeDiscrepancyForm + .control-group#discrepancy-legend + legend Discrepancy log + table#discrepancy-log-table.table.table-bordered.table-hover + form.form-horizontal#form + div.control-group#traveler-legend + legend Traveler + if (traveler.forms.length === 1) + != traveler.forms[0].html + else if (traveler.forms.id(traveler.activeForm)) + != traveler.forms.id(traveler.activeForm).html + else + != '' + hr + div + p + a#location #{traveler.title} + = ' ' + | printed on + = ' ' + span#localtime + + block js + script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') + script(type='text/javascript', src='#{prefix}/bootstrap/js/bootstrap.js') + script(type='text/javascript', src='#{prefix}/datatables/js/jquery.dataTables.js') + script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') + script(type='text/javascript', src='#{prefix}/dependencies/modernizr.js') + script(type='text/javascript', src='#{prefix}/dependencies/holder.js') + script(type='text/javascript', src='#{prefix}/dependencies/binder.js') + script(type='text/javascript', src='#{prefix}/dependencies/moment.js') + script(type='text/javascript', src='#{prefix}/dependencies/livestamp.js') + script(type='text/javascript', src='#{prefix}/dependencies/lodash.js') + script(type='text/javascript', src='#{prefix}/javascripts/validation.js') + script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/ajax-helper.js') + script(type='text/javascript', src='#{prefix}/javascripts/util.js') + script(type='text/javascript', src='#{prefix}/javascripts/traveler-discrepancy-form-loader.js') + script(type='text/javascript', src='#{prefix}/javascripts/traveler-print.js') diff --git a/views/traveler-viewer.jade b/views/traveler-viewer.jade index 812258c2..8f20ae79 100644 --- a/views/traveler-viewer.jade +++ b/views/traveler-viewer.jade @@ -11,6 +11,7 @@ block head block content script(type='text/javascript'). + var traveler = !{JSON.stringify(traveler)}; var prefix = '!{prefix}'; var title = '!{traveler.title}' .container @@ -53,6 +54,9 @@ block content = ' inputs' if (locals.traveler.devices && traveler.devices.length > 0) h4 Tags: !{traveler.devices} + if (locals.traveler.status !== undefined && locals.traveler.status === 2) + .btn-group + a.btn.btn-primary#pdf(data-toggle='tooltip', title='create pdf in print view', href='print', target='#{viewConfig.linkTarget}') Create PDF if (locals.traveler.status !== undefined && locals.traveler.status > 0) .btn-group(data-toggle='buttons-radio') button.btn.btn-info#show-validation Show validation @@ -83,7 +87,13 @@ block content = ' ' span.time #{traveler.updatedOn} br + if locals.traveler.activeDiscrepancyForm + .control-group#discrepancy-legend + legend Discrepancy log + table#discrepancy-log-table.table.table-bordered.table-hover form.form-horizontal#form + div.control-group#traveler-legend + legend Traveler if (traveler.forms.length === 1) != traveler.forms[0].html else if (traveler.forms.id(traveler.activeForm)) @@ -95,8 +105,15 @@ block content block js script(type='text/javascript', src='#{prefix}/jquery/jquery-1.9.1.js') script(type='text/javascript', src='#{prefix}/bootstrap/js/bootstrap.js') + script(type='text/javascript', src='#{prefix}/datatables/js/jquery.dataTables.js') + script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') script(type='text/javascript', src='#{prefix}/dependencies/binder.js') script(type='text/javascript', src='#{prefix}/dependencies/moment.js') + script(type='text/javascript', src='#{prefix}/dependencies/livestamp.js') + script(type='text/javascript', src='#{prefix}/dependencies/lodash.js') script(type='text/javascript', src='#{prefix}/javascripts/validation.js') + script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/ajax-helper.js') script(type='text/javascript', src='#{prefix}/javascripts/util.js') + script(type='text/javascript', src='#{prefix}/javascripts/traveler-discrepancy-form-loader.js') script(type='text/javascript', src='#{prefix}/javascripts/traveler-viewer.js') diff --git a/views/traveler.jade b/views/traveler.jade index 358299a0..da06e0bb 100644 --- a/views/traveler.jade +++ b/views/traveler.jade @@ -11,7 +11,7 @@ block head block content script(type='text/javascript'). - var traveler = !{JSON.stringify(traveler)} + var traveler = !{JSON.stringify(traveler)}; var travelerStatus = !{JSON.stringify(traveler.status)}; var finishedInput = !{JSON.stringify(traveler.finishedInput)}; var totalInput = !{JSON.stringify(traveler.totalInput)}; @@ -81,6 +81,8 @@ block content if (isAdmin) .btn-group button.btn.btn-warning#more More work + .btn-group + a.btn.btn-primary#pdf(data-toggle='tooltip', title='create pdf in print view', href='print', target='#{viewConfig.linkTarget}') Create PDF if (locals.traveler.status !== undefined && locals.traveler.status > 0) //- .btn-group //- button.btn.btn-info#update-progress Update progress @@ -100,7 +102,7 @@ block content #details.collapse.out if (locals.traveler.referenceReleasedForm) h4 - span Created from: + span Created from: a(href='#{prefix}/released-forms/#{locals.traveler.referenceReleasedForm}/', target='#{viewConfig.linkTarget}') Released #{viewConfig.terminology.form} if (locals.traveler.description !== undefined) h4 Description: #{traveler.description} From 648d00397b66f16a3b17fa61cbb20d9ae80837ec Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sat, 30 Jul 2022 09:58:06 -0700 Subject: [PATCH 09/58] Archive 111 (#112) * no ssl now * update version * fix view side nav, release name, pdf in place * add button to view and pdf * add missing js * avoid all break * adjust page layout * add dedicated print view * recover view * switch view * use html2pdf 0.9.3 * fix select in two tables * fix load discrpancy * fix print load discrepancy, print from the window * fix data-livestamp * use abs date * shorter date with time zone * fix time, update print css * include help in validation msg * add help block in print view * default with help, checkbox with rightside label * select all previous released rows by default * all labels and help block before the value --- public/javascripts/form-builder.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index 9c38dfd9..8bd0cd37 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1516,6 +1516,9 @@ function binding_events() { images: 'img.user', }); }, + fnInitComplete() { + fnSelectAll(priorVersionsTable, 'row-selected', 'select-row', true); + }, }); selectMultiEvent('#prior_versions'); filterEvent(); From bd5e3aad815dc16d8e7b5e663d3e38a0cb2143d1 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sat, 30 Jul 2022 10:12:08 -0700 Subject: [PATCH 10/58] json stringify variable in inline js (#113) --- views/admin.jade | 2 -- views/binder-config.jade | 2 -- views/binder.jade | 2 -- views/binders.jade | 2 -- views/currenttravelers.jade | 1 - views/devices.jade | 2 -- views/form-builder.jade | 5 ++--- views/form-config.jade | 2 -- views/form-management.jade | 2 -- views/form-viewer.jade | 3 +-- views/forms.jade | 2 -- views/group.jade | 3 +-- views/layout.jade | 11 ++++++----- views/main.jade | 2 -- views/public-binders.jade | 2 -- views/public-forms.jade | 2 -- views/public-travelers.jade | 2 -- views/released-form.jade | 3 +-- views/released-forms.jade | 2 -- views/report-travelers.jade | 2 -- views/review.jade | 3 --- views/reviews.jade | 2 -- views/share.jade | 3 +-- views/traveler-config.jade | 2 -- views/traveler-viewer.jade | 3 +-- views/traveler.jade | 3 +-- views/travelers.jade | 2 -- views/user.jade | 1 - 28 files changed, 14 insertions(+), 59 deletions(-) diff --git a/views/admin.jade b/views/admin.jade index 03d1edad..8be94fec 100644 --- a/views/admin.jade +++ b/views/admin.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/binder-config.jade b/views/binder-config.jade index ba267fee..4124ad7a 100644 --- a/views/binder-config.jade +++ b/views/binder-config.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/binder.jade b/views/binder.jade index 61587937..4c45334f 100644 --- a/views/binder.jade +++ b/views/binder.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container(style="margin-top: 10px") h3 #{binder.title} != ' ' diff --git a/views/binders.jade b/views/binders.jade index 26132379..992a53a7 100644 --- a/views/binders.jade +++ b/views/binders.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/currenttravelers.jade b/views/currenttravelers.jade index 2b0657c6..3f69fae2 100644 --- a/views/currenttravelers.jade +++ b/views/currenttravelers.jade @@ -12,7 +12,6 @@ block head block content script(type='text/javascript'). var device = !{JSON.stringify(device)}; - var prefix = '!{prefix}'; .container-fluid #message diff --git a/views/devices.jade b/views/devices.jade index 17f80d0e..60496058 100644 --- a/views/devices.jade +++ b/views/devices.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container(style="margin-top: 10px") #message h3 FRIB physical devices diff --git a/views/form-builder.jade b/views/form-builder.jade index 51e1b810..61976f0f 100644 --- a/views/form-builder.jade +++ b/views/form-builder.jade @@ -16,9 +16,8 @@ block head block content script(type='text/javascript'). - var prefix = '!{prefix}'; - var id = '!{id}'; - var released_form_version_mgmt = '!{released_form_version_mgmt}'; + var id = !{JSON.stringify(id)}; + var released_form_version_mgmt = !{JSON.stringify(released_form_version_mgmt)}; var formStatus = !{JSON.stringify(locals.status)} || 0; var formType = !{JSON.stringify(locals.formType)} || 'normal'; .container diff --git a/views/form-config.jade b/views/form-config.jade index d1844b45..93b76824 100644 --- a/views/form-config.jade +++ b/views/form-config.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/form-management.jade b/views/form-management.jade index a4312f17..f52906be 100644 --- a/views/form-management.jade +++ b/views/form-management.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/form-viewer.jade b/views/form-viewer.jade index 87689460..56c689a4 100644 --- a/views/form-viewer.jade +++ b/views/form-viewer.jade @@ -11,8 +11,7 @@ block head block content script(type='text/javascript'). - var prefix = '!{prefix}'; - var title = '!{title}' + var title = !{JSON.stringify(title)}; .container .row .span9 diff --git a/views/forms.jade b/views/forms.jade index 165ac942..78ae1567 100644 --- a/views/forms.jade +++ b/views/forms.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/group.jade b/views/group.jade index d45ecd1a..b11a06c8 100644 --- a/views/group.jade +++ b/views/group.jade @@ -11,8 +11,7 @@ block head block content script(type='text/javascript'). - var prefix = '!{prefix}'; - var group = !{JSON.stringify(group)} + var group = !{JSON.stringify(group)}; .container #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/layout.jade b/views/layout.jade index fc31a599..04cd44aa 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -6,11 +6,12 @@ html body script(type='text/javascript'). var terminology = !{JSON.stringify(viewConfig.terminology)}; - var shareUsers = !{viewConfig.shareUsers}; - var shareGroups = !{viewConfig.shareGroups}; - var linkTarget = '!{viewConfig.linkTarget}' || '_blank'; - var showCCDB = !{viewConfig.showCCDB}; - var userid = '!{locals.session.userid}'; + var shareUsers = !{JSON.stringify(viewConfig.shareUsers)}; + var shareGroups = !{JSON.stringify(viewConfig.shareGroups)}; + var linkTarget = !{JSON.stringify(viewConfig.linkTarget)} || '_blank'; + var showCCDB = !{JSON.stringify(viewConfig.showCCDB)}; + var userid = !{JSON.stringify(locals.session.userid)}; + var prefix = !{JSON.stringify(prefix)}; - var isAdmin = locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('admin') !== -1 - var isManager = locals.session && locals.session.userid && locals.session.roles && locals.session.roles.indexOf('manager') !== -1 diff --git a/views/main.jade b/views/main.jade index 029d069d..b5143283 100644 --- a/views/main.jade +++ b/views/main.jade @@ -9,8 +9,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container(style="margin-top: 20px") .alert.hide .main diff --git a/views/public-binders.jade b/views/public-binders.jade index ba008e9f..6766c350 100644 --- a/views/public-binders.jade +++ b/views/public-binders.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid #message h3 Public accessible binders diff --git a/views/public-forms.jade b/views/public-forms.jade index cf30a3ed..cb0f5590 100644 --- a/views/public-forms.jade +++ b/views/public-forms.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/public-travelers.jade b/views/public-travelers.jade index 3ba2b2dd..2fa90184 100644 --- a/views/public-travelers.jade +++ b/views/public-travelers.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/released-form.jade b/views/released-form.jade index ceb0317e..2c8a3268 100644 --- a/views/released-form.jade +++ b/views/released-form.jade @@ -11,8 +11,7 @@ block head block content script(type='text/javascript'). - var prefix = '!{prefix}'; - var title = '!{title}' + var title = !{JSON.stringify(title)}; .container #message .row diff --git a/views/released-forms.jade b/views/released-forms.jade index 7b72ceb1..d8cf1dbe 100644 --- a/views/released-forms.jade +++ b/views/released-forms.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/report-travelers.jade b/views/report-travelers.jade index 287d327b..a1a9a34d 100644 --- a/views/report-travelers.jade +++ b/views/report-travelers.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container(style="margin-top: 10px") h3 Traveler Report h4 Generated on diff --git a/views/review.jade b/views/review.jade index d15429b4..538c3e4a 100644 --- a/views/review.jade +++ b/views/review.jade @@ -11,9 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; - .container(style="margin-top: 10px") #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/reviews.jade b/views/reviews.jade index aae2c3db..12286156 100644 --- a/views/reviews.jade +++ b/views/reviews.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/share.jade b/views/share.jade index 95627f41..ccad4d30 100644 --- a/views/share.jade +++ b/views/share.jade @@ -12,8 +12,7 @@ block head block content script(type='text/javascript'). - var prefix = '!{prefix}'; - var access = !{access}; + var access = !{JSON.stringify(access)}; .container(style="margin-top: 10px") #message diff --git a/views/traveler-config.jade b/views/traveler-config.jade index c467c11d..c218a1d1 100644 --- a/views/traveler-config.jade +++ b/views/traveler-config.jade @@ -11,8 +11,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container #message h3 Title: diff --git a/views/traveler-viewer.jade b/views/traveler-viewer.jade index 8f20ae79..00685173 100644 --- a/views/traveler-viewer.jade +++ b/views/traveler-viewer.jade @@ -11,9 +11,8 @@ block head block content script(type='text/javascript'). + var title = !{JSON.stringify(traveler.title)}; var traveler = !{JSON.stringify(traveler)}; - var prefix = '!{prefix}'; - var title = '!{traveler.title}' .container #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/traveler.jade b/views/traveler.jade index da06e0bb..a4526e5a 100644 --- a/views/traveler.jade +++ b/views/traveler.jade @@ -15,8 +15,7 @@ block content var travelerStatus = !{JSON.stringify(traveler.status)}; var finishedInput = !{JSON.stringify(traveler.finishedInput)}; var totalInput = !{JSON.stringify(traveler.totalInput)}; - var title = '!{traveler.title}' - var prefix = '!{prefix}'; + var title = !{JSON.stringify(traveler.title)}; .container #message #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') diff --git a/views/travelers.jade b/views/travelers.jade index e53b0838..a29d4ad7 100644 --- a/views/travelers.jade +++ b/views/travelers.jade @@ -10,8 +10,6 @@ block head link(rel='stylesheet', href='#{prefix}/stylesheets/style.css') block content - script(type='text/javascript'). - var prefix = '!{prefix}'; .container-fluid .btn-toolbar .btn-group diff --git a/views/user.jade b/views/user.jade index 3e0ebeed..abd693b7 100644 --- a/views/user.jade +++ b/views/user.jade @@ -12,7 +12,6 @@ block head block content script(type='text/javascript'). var roles = !{JSON.stringify(user.roles)}; - var prefix = '!{prefix}'; .container(style="margin-top: 10px") //- a.btn.btn-primary(href="/users") users #message From 524e4c3b8e4406a800b74074d09b64a0f848ae02 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Mon, 1 Aug 2022 22:01:10 -0700 Subject: [PATCH 11/58] Path alias (#114) * try rewrite * missing / Co-authored-by: Dong Liu --- app.js | 12 ++++++++++++ docker/app.json | 3 +++ package-lock.json | 29 +++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 45 insertions(+) diff --git a/app.js b/app.js index db949203..6d729403 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ const methodOverride = require('method-override'); const expressSession = require('express-session'); const cookieParser = require('cookie-parser'); const errorHandler = require('errorhandler'); +const rewrite = require('express-urlrewrite'); const http = require('http'); const https = require('https'); @@ -151,6 +152,17 @@ if (app.get('env') === 'development') { app.use(errorHandler()); } +// apply app alias routing if any +if (appSettings.alias) { + const fromList = Object.keys(appSettings.alias); + for (let i = 0; i < fromList.length; i += 1) { + const from = fromList[i]; + const to = appSettings.alias[from]; + logger.info(`Route ${from} to ${to}`); + app.use(rewrite(from, to)); + } +} + const routes = require('./routes'); require('./routes/main')(app); diff --git a/docker/app.json b/docker/app.json index fa7d9993..e8dd3ce3 100644 --- a/docker/app.json +++ b/docker/app.json @@ -15,6 +15,9 @@ "cookie_life": 28800000, "body_max_size": "1mb", "upload_size": 5, + "alias": { + "/traveler/formfiles/*" : "/formfiles/$1" + }, "default_form_public_access": -1, "default_traveler_public_access": 0, "default_binder_public_access": 0, diff --git a/package-lock.json b/package-lock.json index 893d7efd..ce36fcd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2165,6 +2165,35 @@ "resolved": "https://registry.npmjs.org/express-slash/-/express-slash-1.0.0.tgz", "integrity": "sha1-4ebOjJEWEt2rU3LBC+AuqIQpgus=" }, + "express-urlrewrite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/express-urlrewrite/-/express-urlrewrite-2.0.0.tgz", + "integrity": "sha512-WU2wariBmEDEVMf+uK3tLbTISQFC8yQ0Wy0tx+KCSs5g1tM+FPClG3bUBtACT4VDyAZrkZEh9lMQX5R9Vfv3Eg==", + "requires": { + "debug": "^4.3.3", + "path-to-regexp": "^6.2.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + } + } + }, "extsprintf": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", diff --git a/package.json b/package.json index 02ac45e0..af71ab77 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "express": "^4.17.1", "express-session": "1.15.6", "express-slash": "1.0.0", + "express-urlrewrite": "^2.0.0", "file-stream-rotator": "^0.5.7", "helmet": "^4.6.0", "jade": "1.10.0", From 200e7e959df84bbfb012999ec77a87930a285aab Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Mon, 1 Aug 2022 22:18:13 -0700 Subject: [PATCH 12/58] convert to string (#115) --- lib/auth.js | 2 +- lib/share.js | 2 +- routes/group.js | 4 ++-- routes/user.js | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index 9de21670..8a8fa9d4 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -361,7 +361,7 @@ function getCurrentUser(userid, req, res, cb) { name: result.displayName, email: result.mail, office: result.physicalDeliveryOfficeName, - phone: result.telephoneNumber, + phone: result.telephoneNumber.toString(), mobile: result.mobile, roles: default_roles, lastVisitedOn: Date.now(), diff --git a/lib/share.js b/lib/share.js index 287d09c4..480e51a9 100644 --- a/lib/share.js +++ b/lib/share.js @@ -52,7 +52,7 @@ function addUserFromAD(req, res, doc) { name: result[0].displayName, email: result[0].mail, office: result[0].physicalDeliveryOfficeName, - phone: result[0].telephoneNumber, + phone: result[0].telephoneNumber.toString(), mobile: result[0].mobile, }); switch (doc.constructor.modelName) { diff --git a/routes/group.js b/routes/group.js index 6cbcafb8..bec1cb28 100644 --- a/routes/group.js +++ b/routes/group.js @@ -92,7 +92,7 @@ function addGroup(req, res) { name: result[0].displayName, email: result[0].mail, office: result[0].physicalDeliveryOfficeName, - phone: result[0].telephoneNumber, + phone: result[0].telephoneNumber.toString(), mobile: result[0].mobile, roles: roles, }); @@ -316,7 +316,7 @@ module.exports = function(app) { name: result[0].displayName, email: result[0].mail, office: result[0].physicalDeliveryOfficeName, - phone: result[0].telephoneNumber, + phone: result[0].telephoneNumber.toString(), mobile: result[0].mobile, roles: [], }); diff --git a/routes/user.js b/routes/user.js index ad616b2e..eb8c449b 100644 --- a/routes/user.js +++ b/routes/user.js @@ -103,7 +103,7 @@ function updateUserProfile(user, res) { name: result[0].displayName, email: result[0].mail, office: result[0].physicalDeliveryOfficeName, - phone: result[0].telephoneNumber, + phone: result[0].telephoneNumber.toString(), mobile: result[0].mobile, }, function(err) { @@ -170,7 +170,7 @@ function storeUser(req, res, result) { name: result.displayName, email: result.mail, office: result.physicalDeliveryOfficeName, - phone: result.telephoneNumber, + phone: result.telephoneNumber.toString(), mobile: result.mobile, roles: roles, }); From df6b88469b7d2b939a2abb0ddfb6ff67226f4339 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sun, 7 Aug 2022 15:29:14 -0700 Subject: [PATCH 13/58] do not load photo (#117) --- public/javascripts/table.js | 20 +++++++++----------- views/layout.jade | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/public/javascripts/table.js b/public/javascripts/table.js index 323535bd..9ef4ab44 100644 --- a/public/javascripts/table.js +++ b/public/javascripts/table.js @@ -106,9 +106,9 @@ function personColumn(title, key) { return data; } if (data) { - return ``; + .toUpperCase()}">`; } return ''; }, @@ -596,11 +596,9 @@ const reviewRequestedByColumn = { } return ``; + .toUpperCase()}" title="${request.requestedBy}">`; }, bFilter: true, }; @@ -620,7 +618,7 @@ const reviewersColumn = { if (type === 'filter' || type === 'sort') { return r._id; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return reviews.join('; '); @@ -664,9 +662,9 @@ const ownerColumn = { return owner; } if (owner) { - return ``; + .toUpperCase()}" title="${owner}">`; } return ''; }, @@ -1105,7 +1103,7 @@ function usersColumn(title, prop) { if (type === 'filter' || type === 'sort') { return u.username; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); @@ -1133,7 +1131,7 @@ function usersFilteredColumn(title, filter) { if (type === 'filter' || type === 'sort') { return u.username; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); diff --git a/views/layout.jade b/views/layout.jade index 04cd44aa..b00b7fbc 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -52,7 +52,7 @@ html ul.nav.pull-right if (locals.session && locals.session.userid) li - img.user(src='#{prefix}/adusers/#{session.userid}/photo', title='#{session.username}', data-src='holder.js/27x40?size=20&text=#{session.username.substr(0, 1).toUpperCase()}') + img.user(title='#{session.username}', src='holder.js/27x40?size=20&text=#{session.username.substr(0, 1).toUpperCase()}') li.dropdown a.dropdown-toggle(href='#', data-toggle='dropdown') #{session.username} b.caret From f63817f5240fc214c23754f85c36bff73c6255de Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Wed, 24 Aug 2022 16:25:48 -0700 Subject: [PATCH 14/58] working only when status 0 (#118) --- public/javascripts/form-builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index 8bd0cd37..103be33e 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1633,7 +1633,7 @@ $(function() { disableAjaxCache(); init(); - if (formStatus === 0 || formStatus === 0.5) { + if (formStatus === 0) { working(); } binding_events(); From d2685d640e6e82d92dd00a85309becfa8e4eace7 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sun, 11 Sep 2022 11:45:08 -0700 Subject: [PATCH 15/58] Import 119 (#122) * clean * remove dup * add form import button * use data src to avoid 404 * form explorer, and support routes --- public/api.html | 239 ---------------------------- public/javascripts/form-builder.js | 40 +++++ public/javascripts/form-explorer.js | 128 +++++++++++++++ public/javascripts/table.js | 32 +++- public/stylesheets/style.css | 6 + routes/form-management.js | 9 ++ routes/form.js | 36 +++++ views/form-builder.jade | 5 +- views/layout.jade | 5 +- 9 files changed, 251 insertions(+), 249 deletions(-) delete mode 100644 public/api.html create mode 100644 public/javascripts/form-explorer.js diff --git a/public/api.html b/public/api.html deleted file mode 100644 index 5c1b5ae7..00000000 --- a/public/api.html +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - FRIB Traveler :: api - - - - - - - - - - -
    -
    -

    FRIB Traveler API

    -

    The application programming interface to access traveler information

    -
    -
    -
    -
    - -
    -
    - -

    The API's are protected by basic authentication. If a client gets 401 response with "api" realm challenge, then the client either did not present the credential in the request or the presented creadential was not right. Please contact the application operator for available credentials.

    -
    -
    - -

    Method: GET

    -

    URL: https://hostname:port/apis/travelers/

    -

    Sample response:

    [
    -  {
    -    "_id": "52f3f98a87d4808008000002",
    -    "createdBy": "liud",
    -    "createdOn": "2014-02-06T21:07:22.730Z",
    -    "devices": [],
    -    "sharedWith": [],
    -    "status": 0,
    -    "title": "update me"
    -  },
    -  {
    -    "_id": "52f8ed88f029d24d2b000002",
    -    "createdBy": "liud",
    -    "createdOn": "2014-02-10T15:17:28.849Z",
    -    "deadline": "2014-02-28T05:00:00.000Z",
    -    "devices": [],
    -    "finishedInput": 4,
    -    "sharedWith": [],
    -    "status": 1.5,
    -    "title": "a long traveler",
    -    "totalInput": 36,
    -    "updatedBy": "liud",
    -    "updatedOn": "2014-03-18T19:12:25.739Z"
    -  },
    -  {
    -    "title": "test77 update",
    -    "status": 0,
    -    "createdBy": "liud",
    -    "createdOn": "2014-03-31T15:34:27.947Z",
    -    "totalInput": 34,
    -    "finishedInput": 0,
    -    "_id": "53398b03951887482f000002",
    -    "sharedWith": [],
    -    "devices": []
    -  },
    -  ...
    -]
    -

    The response will be a JSON array containing the list of travelers. Each traveler in the list is represented by a JSON object with traveler id, title, status, devices, createdBy, clonedBy, createdOn, deadline, updatedBy, updatedOn, sharedWith, finishedInput, and totalInput information. The traveler id can be used to retrieve more details of a traveler, https://hostname:port/apis/travelers/:id/ for the JSON representation and http://hostname:port/travelers/:id/ for the HTML representation.

    -

    The travler list can also be retrieved by a device name.

    -

    URL: https://hostname:port/apis/travelers/?device=:devicename

    -

    where :devicename is the name of device that was assigned to the travelers.

    -
    -
    - -

    Method: GET

    -

    URL: https://hostname:port/apis/travelers/:id/

    -

    where :id is the id of the traveler to be retrieved

    -

    Sample response:

    {
    -  "__v": 26,
    -  "_id": "53bbf46e2ace2f7f111d76c8",
    -  "createdBy": "liud",
    -  "createdOn": "2014-07-08T13:38:54.529Z",
    -  "description": "",
    -  "finishedInput": 2,
    -  "referenceForm": "5283aa947185189f61000001",
    -  "status": 1,
    -  "title": "test validation",
    -  "totalInput": 7,
    -  "updatedBy": "liud",
    -  "updatedOn": "2014-08-12T13:56:02.090Z",
    -  "archived": false,
    -  "notes": [
    -    "53e2380cd48af61751d91394",
    -    "53e38b47d48af61751d91395",
    -    "53e39121d48af61751d91396",
    -    ...
    -  ],
    -  "data": [
    -    "53bbf6b52ace2f7f111d76ca",
    -    "53bbf6cd2ace2f7f111d76cb",
    -    ...
    -  ],
    -  "activeForm": 0,
    -  "forms": [
    -    {
    -      "html": "...",
    -      "_id": "53bbf46e2ace2f7f111d76c9"
    -    }
    -  ],
    -  "sharedWith": [],
    -  "devices": []
    -}
    -

    The traveler details JSON object contains more information than the object in a traveler list. The "forms" property contrains a list of the forms that were used in this traveler. Currently, only one form is allowed for a traveler. The "data" property contrains the data id's that were collected in the traveler. The "notes" property holds the note id's that were inputed in the traveler.

    -
    -
    - -

    Method: GET

    -

    URL: https://hostname:port/apis/travelers/:id/data/

    -

    where :id is the id of the traveler whose data is retrieved

    -

    Sample response:

    [
    -  {
    -    "name": "2f067ecd",
    -    "value": true,
    -    "inputType": "checkbox",
    -    "inputBy": "liud",
    -    "inputOn": "2014-07-08T13:48:37.972Z",
    -    "_id": "53bbf6b52ace2f7f111d76ca"
    -  },
    -  {
    -    "name": "d134f3cd",
    -    "value": "something",
    -    "inputType": "textarea",
    -    "inputBy": "liud",
    -    "inputOn": "2014-07-08T13:49:01.784Z",
    -    "_id": "53bbf6cd2ace2f7f111d76cb"
    -  },
    -  ...
    -]
    -

    Each data item in the list contains the input name in the form, the input type and the value. Besides, it also records who input the value at what time, by which the input history can be generated. For file input, the value is the file's original name when it was uploaded. In order to retrieve the content of the file, the following file API can be used.

    -
    -
    - -

    Method: GET

    -

    URL: https://hostname:port/apis/data/:id/

    -

    where :id is the id of the data whose type is file.

    -
    -
    - -

    Method: GET

    -

    URL: https://hostname:port/apis/travelers/:id/notes/

    -

    where :id is the id of the traveler whose notes are retrieved

    -

    Sample response:

    [
    -  {
    -    "name": "2f067ecd",
    -    "value": "first notes",
    -    "inputBy": "liud",
    -    "inputOn": "2014-08-06T14:13:32.233Z",
    -    "_id": "53e2380cd48af61751d91394"
    -  },
    -  {
    -    "name": "2f067ecd",
    -    "value": "live stamp",
    -    "inputBy": "liud",
    -    "inputOn": "2014-08-07T14:20:55.944Z",
    -    "_id": "53e38b47d48af61751d91395"
    -  },
    -  ...
    -]
    -

    Similar to the data API, each note item in the list contains the input name in the form and the note text. Besides, it also records who input the note at what time, by which the note history can be generated.

    -
    -
    - -

    If you find something not working well or you want a new feature, please contact Dong Liu. Thank you.

    -
    -
    -
    -
    - - - - - diff --git a/public/javascripts/form-builder.js b/public/javascripts/form-builder.js index 103be33e..a2c37078 100644 --- a/public/javascripts/form-builder.js +++ b/public/javascripts/form-builder.js @@ -1415,6 +1415,46 @@ function binding_events() { } }); + $('#import').click(function(e) { + if ($('#output .well.spec').length) { + e.preventDefault(); + modalAlert( + 'Save changes first', + 'The form has been changed. Please save it before this action.' + ); + return; + } + cleanBeforeSave(); + $('#modalLabel').html('Form importer'); + $('#modal .modal-body').empty(); + $('#modal .modal-body').append( + '' + ); + + $('#modal .modal-footer').html( + '' + ); + $('#modal').modal('show'); + FormExplorer.init('.released-forms', '.my-forms', '.form-preview'); + $('#modal button[value="confirm"]').click(function() { + const html = FormExplorer.getHtml(); + if (html !== null) { + $('#output').append(html); + // generate unique name + $('input, textarea').each(function() { + $(this).attr('name', UID.generateShort()); + }); + + // assign id to legent, id is used for side nav + $('legend').each(function() { + $(this).attr('id', UID.generateShort()); + }); + + updateSectionNumbers(); + } + }); + }); + $('#saveas').click(function(e) { e.preventDefault(); if ($('#output .well.spec').length) { diff --git a/public/javascripts/form-explorer.js b/public/javascripts/form-explorer.js new file mode 100644 index 00000000..7f1c96ed --- /dev/null +++ b/public/javascripts/form-explorer.js @@ -0,0 +1,128 @@ +const FormExplorer = (function(parent, $) { + let releasedFormTable = null; + let myFormTable = null; + let formDom = null; + let html = null; + + function retrieveForm(resource, fid, cb) { + $.ajax({ + url: `/${resource}/${fid}/json`, + type: 'GET', + dataType: 'json', + success: function(json) { + cb(json); + }, + }); + } + + function selectEvent() { + $('tbody').on('click', 'a.preview', function preview() { + const row = $(this).closest('tr'); + if (row.hasClass('row-selected')) { + return; + } + $('tr').removeClass('row-selected'); + row.addClass('row-selected'); + + const fid = this.id; + const table = $(this).closest('table'); + let resource = 'forms'; + if (table.hasClass('released-forms')) { + resource = 'released-forms'; + } + + retrieveForm(resource, fid, function(json) { + formDom.fadeTo('slow', 1); + html = json.html || json.base.html; + loadForm(html); + }); + }); + } + + function init(released, my, form) { + const releasedColumns = [ + formExploreColumn, + titleColumn, + formTypeColumn, + releasedOnColumn, + releasedFormColumn, + ]; + fnAddFilterFoot(released, releasedColumns); + releasedFormTable = $(released).dataTable({ + sAjaxSource: '/released-forms/json', + sAjaxDataProp: '', + bProcessing: true, + oLanguage: { + sLoadingRecords: 'Please wait - loading data from the server ...', + }, + aoColumns: releasedColumns, + aaSorting: [[2, 'desc']], + sDom: sDomPage, + }); + const columns = [ + formExploreColumn, + titleColumn, + formTypeColumn, + updatedOnColumn, + formColumn, + ]; + fnAddFilterFoot(my, columns); + myFormTable = $(my).dataTable({ + sAjaxSource: '/myforms/json', + sAjaxDataProp: '', + bProcessing: true, + oLanguage: { + sLoadingRecords: 'Please wait - loading data from the server ...', + }, + aoColumns: columns, + aaSorting: [[2, 'desc']], + sDom: sDomPage, + }); + formDom = $(form); + filterEvent(); + selectEvent(); + } + + // temparary solution for the dirty forms + function cleanForm() { + $('.control-group-buttons', formDom).remove(); + } + + function renderImg() { + formDom.find('img').each(function() { + var $this = $(this); + if ($this.attr('name')) { + if ($this.attr('src') === undefined) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + if ($this.attr('src').indexOf('http') === 0) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + if (prefix && $this.attr('src').indexOf(prefix) !== 0) { + $($this.attr('src', prefix + '/formfiles/' + $this.attr('name'))); + return; + } + } + }); + } + + function renderForm() { + cleanForm(); + renderImg(); + } + + function loadForm() { + formDom.html(html); + renderForm(); + } + + function getHtml() { + return html; + } + + parent.init = init; + parent.getHtml = getHtml; + return parent; +})({}, jQuery); diff --git a/public/javascripts/table.js b/public/javascripts/table.js index 9ef4ab44..28cf0c70 100644 --- a/public/javascripts/table.js +++ b/public/javascripts/table.js @@ -106,7 +106,7 @@ function personColumn(title, key) { return data; } if (data) { - return ``; } @@ -397,6 +397,16 @@ const previewColumn = { sWidth: '25px', }; +const formExploreColumn = { + sTitle: '', + mData: '_id', + bSortable: false, + mRender(data) { + return ``; + }, + sWidth: '25px', +}; + const removeColumn = { sTitle: '', mData: '_id', @@ -426,6 +436,16 @@ const formColumn = { sWidth: '45px', }; +const releasedFormColumn = { + sTitle: 'Link', + mData: '_id', + mRender(data) { + return ``; + }, + bSortable: false, + sWidth: '45px', +}; + const aliasColumn = { sTitle: 'Alias', mData: 'alias', @@ -596,7 +616,7 @@ const reviewRequestedByColumn = { } return ``; }, @@ -618,7 +638,7 @@ const reviewersColumn = { if (type === 'filter' || type === 'sort') { return r._id; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return reviews.join('; '); @@ -662,7 +682,7 @@ const ownerColumn = { return owner; } if (owner) { - return ``; } @@ -1103,7 +1123,7 @@ function usersColumn(title, prop) { if (type === 'filter' || type === 'sort') { return u.username; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); @@ -1131,7 +1151,7 @@ function usersFilteredColumn(title, filter) { if (type === 'filter' || type === 'sort') { return u.username; } - return ``; + return ``; }); if (type === 'filter' || type === 'sort') { return names.join('; '); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 308bf866..b628b502 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -230,6 +230,12 @@ text { overflow-y: visible; } +.modal-lg { + width: 80%; + margin-left: -40%; + max-height: calc(100vh - 212px); +} + /* builder stuff */ ul.dropdown-menu input[type='checkbox'] { margin-left: 0; diff --git a/routes/form-management.js b/routes/form-management.js index 14431575..45914009 100644 --- a/routes/form-management.js +++ b/routes/form-management.js @@ -224,4 +224,13 @@ module.exports = function(app) { }); } ); + + app.get( + '/released-forms/:id/json', + auth.ensureAuthenticated, + reqUtils.exist('id', ReleasedForm), + function(req, res) { + return res.status(200).json(req[req.params.id]); + } + ); }; diff --git a/routes/form.js b/routes/form.js index 18b2a6da..57cfee95 100644 --- a/routes/form.js +++ b/routes/form.js @@ -63,6 +63,42 @@ module.exports = function(app) { } }); + app.get('/myforms/json', auth.ensureAuthenticated, async function(req, res) { + try { + const me = await User.findOne( + { + _id: req.session.userid, + }, + 'forms' + ).exec(); + if (!me) { + return res.status(400).send('cannot identify the current user'); + } + const forms = await Form.find( + { + $or: [ + { + createdBy: req.session.userid, + }, + { + owner: req.session.userid, + }, + { + _id: { + $in: me.forms, + }, + }, + ], + }, + 'title formType status updatedOn' + ).exec(); + return res.status(200).json(forms); + } catch (error) { + logger.error(error); + return res.status(500).send(error.message); + } + }); + // forms owned by the user that are under review app.get('/submittedforms/json', auth.ensureAuthenticated, async function( req, diff --git a/views/form-builder.jade b/views/form-builder.jade index 61976f0f..6de183aa 100644 --- a/views/form-builder.jade +++ b/views/form-builder.jade @@ -22,7 +22,7 @@ block content var formType = !{JSON.stringify(locals.formType)} || 'normal'; .container #message - #modal.modal.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') + #modal.modal.modal-lg.hide.fade(tabindex='-1',role='dialog',data-backdrop='static',aria-labelledby='modalLabel',aria-hidden='true') .modal-header h3#modalLabel .modal-body @@ -126,6 +126,8 @@ block content .btn-group a.btn.btn-info#preview(data-toggle='tooltip', title='the form needs be saved first', href='preview', target='#{viewConfig.linkTarget}') Preview if (status === 0) + .btn-group + button.btn.btn-primary#import(data-toggle='tooltip', title='import the content of a form') Import .btn-group button.btn.btn-primary#saveas(data-toggle='tooltip', title='create a copy of current form') Save as if (status === 0) @@ -159,4 +161,5 @@ block js script(type='text/javascript', src='#{prefix}/datatables/js/TableTools.js') script(type='text/javascript', src='#{prefix}/datatables/js/dataTables.bootstrap.js') script(type='text/javascript', src='#{prefix}/javascripts/table.js') + script(type='text/javascript', src='#{prefix}/javascripts/form-explorer.js') script(type='text/javascript', src='#{prefix}/javascripts/form-builder.js') diff --git a/views/layout.jade b/views/layout.jade index b00b7fbc..6e22e58f 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -4,6 +4,7 @@ html meta(http-equiv='X-UA-Compatible', content='IE=Edge') block head body + script(type='text/javascript', src='#{prefix}/dependencies/holder.js') script(type='text/javascript'). var terminology = !{JSON.stringify(viewConfig.terminology)}; var shareUsers = !{JSON.stringify(viewConfig.shareUsers)}; @@ -52,7 +53,7 @@ html ul.nav.pull-right if (locals.session && locals.session.userid) li - img.user(title='#{session.username}', src='holder.js/27x40?size=20&text=#{session.username.substr(0, 1).toUpperCase()}') + img.user(title='#{session.username}', data-src='holder.js/27x40?size=20&text=#{session.username.substr(0, 1).toUpperCase()}') li.dropdown a.dropdown-toggle(href='#', data-toggle='dropdown') #{session.username} b.caret @@ -70,7 +71,5 @@ html .container-fluid h6.text-right a(href='https://github.com/dongliu/traveler', target='_blank') Release #{viewConfig.appVersion} - - script(type='text/javascript', src='#{prefix}/dependencies/holder.js') block js From 50d75cdffcafbe90c22289c5ea4c1a074e09f3bb Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Sun, 6 Nov 2022 16:38:51 -0800 Subject: [PATCH 16/58] Fix form config 104 (#123) * edit the text * display html in form title and desc * display html in traveler title * name in title --- public/javascripts/form-config.js | 2 +- public/javascripts/traveler-config.js | 2 +- public/jquery/jquery.jeditable.js | 1024 +++++++++++++------------ views/form-builder.jade | 4 +- views/form-config.jade | 4 +- views/traveler-config.jade | 4 +- views/traveler-print.jade | 2 +- views/traveler.jade | 2 +- 8 files changed, 535 insertions(+), 509 deletions(-) diff --git a/public/javascripts/form-config.js b/public/javascripts/form-config.js index 87741496..6caf36fa 100644 --- a/public/javascripts/form-config.js +++ b/public/javascripts/form-config.js @@ -38,7 +38,7 @@ $(function() { data: JSON.stringify(data), success: function(json) { initValue[that.id] = json[that.id]; - $(that).text(json[that.id]); + $(that).html(json[that.id]); }, error: function(jqXHR) { $(that).text(initValue[that.id]); diff --git a/public/javascripts/traveler-config.js b/public/javascripts/traveler-config.js index ea54a567..cfa2d59c 100644 --- a/public/javascripts/traveler-config.js +++ b/public/javascripts/traveler-config.js @@ -54,7 +54,7 @@ $(function() { data: JSON.stringify(data), success: function(json) { initValue[that.id] = json[that.id]; - $(that).text(json[that.id]); + $(that).html(json[that.id]); }, error: function(jqXHR) { $(that).text(initValue[that.id]); diff --git a/public/jquery/jquery.jeditable.js b/public/jquery/jquery.jeditable.js index 003000ed..d1aabd40 100644 --- a/public/jquery/jquery.jeditable.js +++ b/public/jquery/jquery.jeditable.js @@ -15,529 +15,555 @@ */ /** - * Version 1.7.1 - * - * ** means there is basic unit tests for this parameter. - * - * @name Jeditable - * @type jQuery - * @param String target (POST) URL or function to send edited content to ** - * @param Hash options additional options - * @param String options[method] method to use to send edited content (POST or PUT) ** - * @param Function options[callback] Function to run after submitting edited content ** - * @param String options[name] POST parameter name of edited content - * @param String options[id] POST parameter name of edited div id - * @param Hash options[submitdata] Extra parameters to send when submitting edited content. - * @param String options[type] text, textarea or select (or any 3rd party input type) ** - * @param Integer options[rows] number of rows if using textarea ** - * @param Integer options[cols] number of columns if using textarea ** - * @param Mixed options[height] 'auto', 'none' or height in pixels ** - * @param Mixed options[width] 'auto', 'none' or width in pixels ** - * @param String options[loadurl] URL to fetch input content before editing ** - * @param String options[loadtype] Request type for load url. Should be GET or POST. - * @param String options[loadtext] Text to display while loading external content. - * @param Mixed options[loaddata] Extra parameters to pass when fetching content before editing. - * @param Mixed options[data] Or content given as paramameter. String or function.** - * @param String options[indicator] indicator html to show when saving - * @param String options[tooltip] optional tooltip text via title attribute ** - * @param String options[event] jQuery event such as 'click' of 'dblclick' ** - * @param String options[submit] submit button value, empty means no button ** - * @param String options[cancel] cancel button value, empty means no button ** - * @param String options[cssclass] CSS class to apply to input form. 'inherit' to copy from parent. ** - * @param String options[style] Style to apply to input form 'inherit' to copy from parent. ** - * @param String options[select] true or false, when true text is highlighted ?? - * @param String options[placeholder] Placeholder text or html to insert when element is empty. ** - * @param String options[onblur] 'cancel', 'submit', 'ignore' or function ?? - * - * @param Function options[onsubmit] function(settings, original) { ... } called before submit - * @param Function options[onreset] function(settings, original) { ... } called before reset - * @param Function options[onerror] function(settings, original, xhr) { ... } called on error - * - * @param Hash options[ajaxoptions] jQuery Ajax options. See docs.jquery.com. - * - */ + * Version 1.7.1 + * + * ** means there is basic unit tests for this parameter. + * + * @name Jeditable + * @type jQuery + * @param String target (POST) URL or function to send edited content to ** + * @param Hash options additional options + * @param String options[method] method to use to send edited content (POST or PUT) ** + * @param Function options[callback] Function to run after submitting edited content ** + * @param String options[name] POST parameter name of edited content + * @param String options[id] POST parameter name of edited div id + * @param Hash options[submitdata] Extra parameters to send when submitting edited content. + * @param String options[type] text, textarea or select (or any 3rd party input type) ** + * @param Integer options[rows] number of rows if using textarea ** + * @param Integer options[cols] number of columns if using textarea ** + * @param Mixed options[height] 'auto', 'none' or height in pixels ** + * @param Mixed options[width] 'auto', 'none' or width in pixels ** + * @param String options[loadurl] URL to fetch input content before editing ** + * @param String options[loadtype] Request type for load url. Should be GET or POST. + * @param String options[loadtext] Text to display while loading external content. + * @param Mixed options[loaddata] Extra parameters to pass when fetching content before editing. + * @param Mixed options[data] Or content given as paramameter. String or function.** + * @param String options[indicator] indicator html to show when saving + * @param String options[tooltip] optional tooltip text via title attribute ** + * @param String options[event] jQuery event such as 'click' of 'dblclick' ** + * @param String options[submit] submit button value, empty means no button ** + * @param String options[cancel] cancel button value, empty means no button ** + * @param String options[cssclass] CSS class to apply to input form. 'inherit' to copy from parent. ** + * @param String options[style] Style to apply to input form 'inherit' to copy from parent. ** + * @param String options[select] true or false, when true text is highlighted ?? + * @param String options[placeholder] Placeholder text or html to insert when element is empty. ** + * @param String options[onblur] 'cancel', 'submit', 'ignore' or function ?? + * + * @param Function options[onsubmit] function(settings, original) { ... } called before submit + * @param Function options[onreset] function(settings, original) { ... } called before reset + * @param Function options[onerror] function(settings, original, xhr) { ... } called on error + * + * @param Hash options[ajaxoptions] jQuery Ajax options. See docs.jquery.com. + * + */ (function($) { + $.fn.editable = function(target, options) { + if ('disable' == target) { + $(this).data('disabled.editable', true); + return; + } + if ('enable' == target) { + $(this).data('disabled.editable', false); + return; + } + if ('destroy' == target) { + $(this) + .unbind($(this).data('event.editable')) + .removeData('disabled.editable') + .removeData('event.editable'); + return; + } + + var settings = $.extend( + {}, + $.fn.editable.defaults, + { target: target }, + options + ); + + /* setup some functions */ + var plugin = $.editable.types[settings.type].plugin || function() {}; + var submit = $.editable.types[settings.type].submit || function() {}; + var buttons = + $.editable.types[settings.type].buttons || + $.editable.types['defaults'].buttons; + var content = + $.editable.types[settings.type].content || + $.editable.types['defaults'].content; + var element = + $.editable.types[settings.type].element || + $.editable.types['defaults'].element; + var reset = + $.editable.types[settings.type].reset || + $.editable.types['defaults'].reset; + var callback = settings.callback || function() {}; + var onedit = settings.onedit || function() {}; + var onsubmit = settings.onsubmit || function() {}; + var onreset = settings.onreset || function() {}; + var onerror = settings.onerror || reset; + + /* show tooltip */ + if (settings.tooltip) { + $(this).attr('title', settings.tooltip); + } + + settings.autowidth = 'auto' == settings.width; + settings.autoheight = 'auto' == settings.height; + + return this.each(function() { + /* save this to self because this changes when scope changes */ + var self = this; + + /* inlined block elements lose their width and height after first edit */ + /* save them for later use as workaround */ + var savedwidth = $(self).width(); + var savedheight = $(self).height(); + + /* save so it can be later used by $.editable('destroy') */ + $(this).data('event.editable', settings.event); - $.fn.editable = function(target, options) { - - if ('disable' == target) { - $(this).data('disabled.editable', true); - return; + /* if element is empty add something clickable (if requested) */ + if (!$.trim($(this).html())) { + $(this).html(settings.placeholder); + } + + $(this).bind(settings.event, function(e) { + /* abort if disabled for this element */ + if (true === $(this).data('disabled.editable')) { + return; } - if ('enable' == target) { - $(this).data('disabled.editable', false); - return; + + /* prevent throwing an exeption if edit field is clicked again */ + if (self.editing) { + return; } - if ('destroy' == target) { - $(this) - .unbind($(this).data('event.editable')) - .removeData('disabled.editable') - .removeData('event.editable'); - return; + + /* abort if onedit hook returns false */ + if (false === onedit.apply(this, [settings, self])) { + return; } - - var settings = $.extend({}, $.fn.editable.defaults, {target:target}, options); - - /* setup some functions */ - var plugin = $.editable.types[settings.type].plugin || function() { }; - var submit = $.editable.types[settings.type].submit || function() { }; - var buttons = $.editable.types[settings.type].buttons - || $.editable.types['defaults'].buttons; - var content = $.editable.types[settings.type].content - || $.editable.types['defaults'].content; - var element = $.editable.types[settings.type].element - || $.editable.types['defaults'].element; - var reset = $.editable.types[settings.type].reset - || $.editable.types['defaults'].reset; - var callback = settings.callback || function() { }; - var onedit = settings.onedit || function() { }; - var onsubmit = settings.onsubmit || function() { }; - var onreset = settings.onreset || function() { }; - var onerror = settings.onerror || reset; - - /* show tooltip */ + + /* prevent default action and bubbling */ + e.preventDefault(); + e.stopPropagation(); + + /* remove tooltip */ if (settings.tooltip) { - $(this).attr('title', settings.tooltip); + $(self).removeAttr('title'); } - - settings.autowidth = 'auto' == settings.width; - settings.autoheight = 'auto' == settings.height; - - return this.each(function() { - - /* save this to self because this changes when scope changes */ - var self = this; - - /* inlined block elements lose their width and height after first edit */ - /* save them for later use as workaround */ - var savedwidth = $(self).width(); - var savedheight = $(self).height(); - - /* save so it can be later used by $.editable('destroy') */ - $(this).data('event.editable', settings.event); - - /* if element is empty add something clickable (if requested) */ - if (!$.trim($(this).html())) { - $(this).html(settings.placeholder); - } - - $(this).bind(settings.event, function(e) { - - /* abort if disabled for this element */ - if (true === $(this).data('disabled.editable')) { - return; - } - - /* prevent throwing an exeption if edit field is clicked again */ - if (self.editing) { - return; - } - - /* abort if onedit hook returns false */ - if (false === onedit.apply(this, [settings, self])) { - return; - } - - /* prevent default action and bubbling */ - e.preventDefault(); - e.stopPropagation(); - - /* remove tooltip */ - if (settings.tooltip) { - $(self).removeAttr('title'); + + /* figure out how wide and tall we are, saved width and height */ + /* are workaround for http://dev.jquery.com/ticket/2190 */ + if (0 == $(self).width()) { + //$(self).css('visibility', 'hidden'); + settings.width = savedwidth; + settings.height = savedheight; + } else { + if (settings.width != 'none') { + settings.width = settings.autowidth + ? $(self).width() + : settings.width; + } + if (settings.height != 'none') { + settings.height = settings.autoheight + ? $(self).height() + : settings.height; + } + } + //$(this).css('visibility', ''); + + /* remove placeholder text, replace is here because of IE */ + if ( + $(this) + .html() + .toLowerCase() + .replace(/(;|")/g, '') == + settings.placeholder.toLowerCase().replace(/(;|")/g, '') + ) { + $(this).html(''); + } + + self.editing = true; + // edit the text as is, not edit the html + self.revert = $(self).text(); + $(self).html(''); + + /* create the form object */ + var form = $('
    '); + + /* apply css or style or both */ + if (settings.cssclass) { + if ('inherit' == settings.cssclass) { + form.attr('class', $(self).attr('class')); + } else { + form.attr('class', settings.cssclass); + } + } + + if (settings.style) { + if ('inherit' == settings.style) { + form.attr('style', $(self).attr('style')); + /* IE needs the second line or display wont be inherited */ + form.css('display', $(self).css('display')); + } else { + form.attr('style', settings.style); + } + } + + /* add main input element to form and store it in input */ + var input = element.apply(form, [settings, self]); + + /* set input content via POST, GET, given data or existing value */ + var input_content; + + if (settings.loadurl) { + var t = setTimeout(function() { + input.disabled = true; + content.apply(form, [settings.loadtext, settings, self]); + }, 100); + + var loaddata = {}; + loaddata[settings.id] = self.id; + if ($.isFunction(settings.loaddata)) { + $.extend( + loaddata, + settings.loaddata.apply(self, [self.revert, settings]) + ); + } else { + $.extend(loaddata, settings.loaddata); + } + $.ajax({ + type: settings.loadtype, + url: settings.loadurl, + data: loaddata, + async: false, + success: function(result) { + window.clearTimeout(t); + input_content = result; + input.disabled = false; + }, + }); + } else if (settings.data) { + input_content = settings.data; + if ($.isFunction(settings.data)) { + input_content = settings.data.apply(self, [self.revert, settings]); + } + } else { + input_content = self.revert; + } + content.apply(form, [input_content, settings, self]); + + input.attr('name', settings.name); + + /* add buttons to the form */ + buttons.apply(form, [settings, self]); + + /* add created form to self */ + $(self).append(form); + + /* attach 3rd party plugin if requested */ + plugin.apply(form, [settings, self]); + + /* focus to first visible form element */ + $(':input:visible:enabled:first', form).focus(); + + /* highlight input contents when requested */ + if (settings.select) { + input.select(); + } + + /* discard changes if pressing esc */ + input.keydown(function(e) { + if (e.keyCode == 27) { + e.preventDefault(); + //self.reset(); + reset.apply(form, [settings, self]); + } + }); + + /* discard, submit or nothing with changes when clicking outside */ + /* do nothing is usable when navigating with tab */ + var t; + if ('cancel' == settings.onblur) { + input.blur(function(e) { + /* prevent canceling if submit was clicked */ + t = setTimeout(function() { + reset.apply(form, [settings, self]); + }, 500); + }); + } else if ('submit' == settings.onblur) { + input.blur(function(e) { + /* prevent double submit if submit was clicked */ + t = setTimeout(function() { + form.submit(); + }, 200); + }); + } else if ($.isFunction(settings.onblur)) { + input.blur(function(e) { + settings.onblur.apply(self, [input.val(), settings]); + }); + } else { + input.blur(function(e) { + /* TODO: maybe something here */ + }); + } + + form.submit(function(e) { + if (t) { + clearTimeout(t); + } + + /* do no submit */ + e.preventDefault(); + + /* call before submit hook. */ + /* if it returns false abort submitting */ + + if (false !== onsubmit.apply(form, [settings, self])) { + /* custom inputs call before submit hook. */ + /* if it returns false abort submitting */ + if (false !== submit.apply(form, [settings, self])) { + /* check if given target is function */ + if ($.isFunction(settings.target)) { + var str = settings.target.apply(self, [input.val(), settings]); + $(self).html(str); + self.editing = false; + callback.apply(self, [self.innerHTML, settings]); + /* TODO: this is not dry */ + + if (!$.trim($(self).html())) { + $(self).html(settings.placeholder); } - - /* figure out how wide and tall we are, saved width and height */ - /* are workaround for http://dev.jquery.com/ticket/2190 */ - if (0 == $(self).width()) { - //$(self).css('visibility', 'hidden'); - settings.width = savedwidth; - settings.height = savedheight; + } else { + /* add edited content and id of edited element to POST */ + var submitdata = {}; + submitdata[settings.name] = input.val(); + submitdata[settings.id] = self.id; + /* add extra data to be POST:ed */ + if ($.isFunction(settings.submitdata)) { + $.extend( + submitdata, + settings.submitdata.apply(self, [self.revert, settings]) + ); } else { - if (settings.width != 'none') { - settings.width = - settings.autowidth ? $(self).width() : settings.width; - } - if (settings.height != 'none') { - settings.height = - settings.autoheight ? $(self).height() : settings.height; - } - } - //$(this).css('visibility', ''); - - /* remove placeholder text, replace is here because of IE */ - if ($(this).html().toLowerCase().replace(/(;|")/g, '') == - settings.placeholder.toLowerCase().replace(/(;|")/g, '')) { - $(this).html(''); - } - - self.editing = true; - self.revert = $(self).html(); - $(self).html(''); - - /* create the form object */ - var form = $(''); - - /* apply css or style or both */ - if (settings.cssclass) { - if ('inherit' == settings.cssclass) { - form.attr('class', $(self).attr('class')); - } else { - form.attr('class', settings.cssclass); - } + $.extend(submitdata, settings.submitdata); } - if (settings.style) { - if ('inherit' == settings.style) { - form.attr('style', $(self).attr('style')); - /* IE needs the second line or display wont be inherited */ - form.css('display', $(self).css('display')); - } else { - form.attr('style', settings.style); - } + /* quick and dirty PUT support */ + if ('PUT' == settings.method) { + submitdata['_method'] = 'put'; } - /* add main input element to form and store it in input */ - var input = element.apply(form, [settings, self]); - - /* set input content via POST, GET, given data or existing value */ - var input_content; - - if (settings.loadurl) { - var t = setTimeout(function() { - input.disabled = true; - content.apply(form, [settings.loadtext, settings, self]); - }, 100); - - var loaddata = {}; - loaddata[settings.id] = self.id; - if ($.isFunction(settings.loaddata)) { - $.extend(loaddata, settings.loaddata.apply(self, [self.revert, settings])); - } else { - $.extend(loaddata, settings.loaddata); - } - $.ajax({ - type : settings.loadtype, - url : settings.loadurl, - data : loaddata, - async : false, - success: function(result) { - window.clearTimeout(t); - input_content = result; - input.disabled = false; - } - }); - } else if (settings.data) { - input_content = settings.data; - if ($.isFunction(settings.data)) { - input_content = settings.data.apply(self, [self.revert, settings]); + /* show the saving indicator */ + $(self).html(settings.indicator); + + /* defaults for ajaxoptions */ + var ajaxoptions = { + type: 'POST', + data: submitdata, + dataType: 'html', + url: settings.target, + success: function(result, status) { + if (ajaxoptions.dataType == 'html') { + $(self).html(result); } - } else { - input_content = self.revert; - } - content.apply(form, [input_content, settings, self]); - - input.attr('name', settings.name); - - /* add buttons to the form */ - buttons.apply(form, [settings, self]); - - /* add created form to self */ - $(self).append(form); - - /* attach 3rd party plugin if requested */ - plugin.apply(form, [settings, self]); - - /* focus to first visible form element */ - $(':input:visible:enabled:first', form).focus(); - - /* highlight input contents when requested */ - if (settings.select) { - input.select(); - } - - /* discard changes if pressing esc */ - input.keydown(function(e) { - if (e.keyCode == 27) { - e.preventDefault(); - //self.reset(); - reset.apply(form, [settings, self]); + self.editing = false; + callback.apply(self, [result, settings]); + if (!$.trim($(self).html())) { + $(self).html(settings.placeholder); } - }); - - /* discard, submit or nothing with changes when clicking outside */ - /* do nothing is usable when navigating with tab */ - var t; - if ('cancel' == settings.onblur) { - input.blur(function(e) { - /* prevent canceling if submit was clicked */ - t = setTimeout(function() { - reset.apply(form, [settings, self]); - }, 500); - }); - } else if ('submit' == settings.onblur) { - input.blur(function(e) { - /* prevent double submit if submit was clicked */ - t = setTimeout(function() { - form.submit(); - }, 200); - }); - } else if ($.isFunction(settings.onblur)) { - input.blur(function(e) { - settings.onblur.apply(self, [input.val(), settings]); - }); - } else { - input.blur(function(e) { - /* TODO: maybe something here */ - }); - } + }, + error: function(xhr, status, error) { + onerror.apply(form, [settings, self, xhr]); + }, + }; - form.submit(function(e) { + /* override with what is given in settings.ajaxoptions */ + $.extend(ajaxoptions, settings.ajaxoptions); + $.ajax(ajaxoptions); + } + } + } - if (t) { - clearTimeout(t); - } + /* show tooltip again */ + $(self).attr('title', settings.tooltip); - /* do no submit */ - e.preventDefault(); - - /* call before submit hook. */ - /* if it returns false abort submitting */ - if (false !== onsubmit.apply(form, [settings, self])) { - /* custom inputs call before submit hook. */ - /* if it returns false abort submitting */ - if (false !== submit.apply(form, [settings, self])) { - - /* check if given target is function */ - if ($.isFunction(settings.target)) { - var str = settings.target.apply(self, [input.val(), settings]); - $(self).html(str); - self.editing = false; - callback.apply(self, [self.innerHTML, settings]); - /* TODO: this is not dry */ - if (!$.trim($(self).html())) { - $(self).html(settings.placeholder); - } - } else { - /* add edited content and id of edited element to POST */ - var submitdata = {}; - submitdata[settings.name] = input.val(); - submitdata[settings.id] = self.id; - /* add extra data to be POST:ed */ - if ($.isFunction(settings.submitdata)) { - $.extend(submitdata, settings.submitdata.apply(self, [self.revert, settings])); - } else { - $.extend(submitdata, settings.submitdata); - } - - /* quick and dirty PUT support */ - if ('PUT' == settings.method) { - submitdata['_method'] = 'put'; - } - - /* show the saving indicator */ - $(self).html(settings.indicator); - - /* defaults for ajaxoptions */ - var ajaxoptions = { - type : 'POST', - data : submitdata, - dataType: 'html', - url : settings.target, - success : function(result, status) { - if (ajaxoptions.dataType == 'html') { - $(self).html(result); - } - self.editing = false; - callback.apply(self, [result, settings]); - if (!$.trim($(self).html())) { - $(self).html(settings.placeholder); - } - }, - error : function(xhr, status, error) { - onerror.apply(form, [settings, self, xhr]); - } - }; - - /* override with what is given in settings.ajaxoptions */ - $.extend(ajaxoptions, settings.ajaxoptions); - $.ajax(ajaxoptions); - - } - } - } - - /* show tooltip again */ - $(self).attr('title', settings.tooltip); - - return false; - }); - }); - - /* privileged methods */ - this.reset = function(form) { - /* prevent calling reset twice when blurring */ - if (this.editing) { - /* before reset hook, if it returns false abort reseting */ - if (false !== onreset.apply(form, [settings, self])) { - $(self).html(self.revert); - self.editing = false; - if (!$.trim($(self).html())) { - $(self).html(settings.placeholder); - } - /* show tooltip again */ - if (settings.tooltip) { - $(self).attr('title', settings.tooltip); - } - } - } - }; + return false; }); + }); - }; - - - $.editable = { - types: { - defaults: { - element : function(settings, original) { - var input = $(''); - $(this).append(input); - return(input); - }, - content : function(string, settings, original) { - $(':input:first', this).val(string); - }, - reset : function(settings, original) { - original.reset(this); - }, - buttons : function(settings, original) { - var form = this; - if (settings.submit) { - /* if given html string use that */ - if (settings.submit.match(/>$/)) { - var submit = $(settings.submit).click(function() { - if (submit.attr("type") != "submit") { - form.submit(); - } - }); - /* otherwise use button with given string as text */ - } else { - var submit = $('