diff --git a/.codeclimate.yml b/.codeclimate.yml index 41cf204cc..2851f2f2d 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,7 +1,4 @@ languages: - Ruby: true JavaScript: true - PHP: true - Python: true exclude_paths: -- "public/js/bootstrap-datepicker.js" + - "public/js/bootstrap-datepicker.js" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..786993626 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +.vscode +docs +config.example/ +scripts/ +import/ +export/ +mock_data/ +import_data/ +node_modules/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..cd4ff6adc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.js] +quote_type = single diff --git a/.env.dev b/.env.dev new file mode 100644 index 000000000..d71bf8225 --- /dev/null +++ b/.env.dev @@ -0,0 +1,18 @@ +DB_ROOT_PASSWORD=rootpass +DB_DATABASE=pgdb +DB_USERNAME=pguser +DB_PASSWORD=pgpass +DB_HOST=localhost +DB_DIALECT=postgres +DB_PORT=5432 +DB_URL=postgres://pguser:pgpass@localhost:5432/pgdb # set to match your database +USE_SSL=false +# DB_SSL_REQUIRE=false +# DB_SSL_REJECT_UNAUTHORIZED=false +OPTION_ALLOW_NEW_REGISTRATIONS=true +# SMTP_AUTH_PASS='' +# SMTP_AUTH_USER='' +# SMTP_FROM='' +SMTP_HOST='' +# SMTP_PORT='' +# SMTP_REQUIRE_TLS='' diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..bd24439f5 --- /dev/null +++ b/.env.example @@ -0,0 +1,46 @@ +## ENV FILE + +## copy this file save as .env +## run `docker compose up -d --build` +## should work as-is, modify based on use-case, some notes below: + +### URL STUFFS +# BRANDING_URI=https://timeoff.management +# BRANDING_WEBSITE=https://promotional.website +# HEADER_TITLE="Timeoff.Management" +# CONTACT_EMAIL_ADDRESS=email@ddre.ss + +## DB STUFFS +# You can get away with setting below only if using external db +# i.e. neon, supabase, etc +DATABASE_URL=postgres://user:pass@postgres/dbname # set to match your database +# APP_PORT=3001 + +## these below should be set if using docker db or other local (non-hosted) db +## if using url above, make sure they match +DB_DATABASE=dbname +DB_USER=user +DB_PASSWORD=pass +DB_HOST=postgres +# DB_DIALECT=mysql # mysql, postgres, sqlite, mssql +# DB_PORT=3306 # 5432, 1433 +# DB_ROOT_PASS=changeme + +## SSL STUFFS for hosted db mostly +# USE_SSL=false +# DB_SSL_REQUIRE=true +# DB_SSL_REJECT_UNAUTHORIZED=true + +### REG and API stuffs +OPTION_ALLOW_NEW_REGISTRATIONS=false # set to true for initial setup +# API_KEY=changeme + +### EMAIL STUFFS +# SEND_EMAIL=false +# SMTP_AUTH_REQUIRED=true # false=user/pass not required for SMTP server +# SMTP_AUTH_PASS=changeme +# SMTP_AUTH_USER=username +# SMTP_FROM=emailname +# SMTP_HOST=smtp.example.com +# SMTP_PORT=587 +# SMTP_REQUIRE_TLS=true diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..6437ca7df --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es2021": true + }, + "extends": [ + "standard", + "prettier", + "plugin:mocha/recommended" + ], + "parserOptions": { + "ecmaVersion": "latest" + }, + "plugins": [ + "mocha" + ], + "rules": { + "camelcase": 0, + "space-before-function-paren": 0, + "arrow-body-style": ["error", "as-needed"], + //"prefer-arrow-callback": [ + // "error", + // { "allowNamedFunctions": true } + //], + "func-style": [ + "error", + "declaration", + { "allowArrowFunctions": true } + ] + + } +} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 000000000..76b71bcca --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,38 @@ +name: Publish Docker image + +on: + push: + branches: + - master + - release/* + release: + types: [published] + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: aliengen/timeoff-management-application + + - name: Build and push Docker image + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 1d1d2ef66..8d7b29626 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,12 @@ *.swn *.swm node_modules/ +config/ +mock_data/ +import_data/ +import/ +export/ +.env *.sqlite +*.DS_Store +t/integration/test.csv diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..6807bd549 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,11 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) +# and commit this file to your remote git repository to share the goodness with others. + +# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart + +tasks: + - init: bootstrap + command: sh ./bootstrap.sh + + diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9f8ecd9f0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "singleQuote": true, + "arrowParens": "avoid", + "trailingComma": "none" +} diff --git a/.travis.yml b/.travis.yml index 8c5e501c6..6479314a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,16 @@ language: node_js node_js: - - "0.10" - - "0.12" - - "v4.8.3" - - "v6.10.1" + - 'v10.16.3' sudo: false +addons: + chrome: stable cache: directories: - node_modules env: -before_install: + - USE_CHROME=1 before_script: + - wget https://chromedriver.storage.googleapis.com/77.0.3865.40/chromedriver_linux64.zip + - cd bin; unzip ../chromedriver_linux64.zip; cd ../ + - export PATH=$PWD/bin:$PATH - node bin/wwww > /dev/null 2>&1 & diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..6043cb7cd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "runtimeExecutable": "npm", + "runtimeArgs": ["start"], + "skipFiles": ["/**"], + "console": "integratedTerminal", + "envFile": "${workspaceFolder}/.env" + }, + { + "type": "node", + "request": "attach", + "name": "Docker: Attach to Node", + "port": 9229, + "address": "localhost", + "localRoot": "${workspaceFolder}", + "remoteRoot": "/usr/src/app" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..a2f7461e3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,25 @@ +{ + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.addMissingImports": "explicit", + "source.fixAll.eslint": "explicit" + }, + "search.exclude": { + "**/node_modules": true, + "**/.vscode": true, + "**/build": true, + "**/dist": true + }, + "git.autofetch": true, + "editor.trimAutoWhitespace": true, + "files.encoding": "utf8", + "files.trimFinalNewlines": false, + "files.trimTrailingWhitespace": true, + "css.validate": false, + "editor.quickSuggestions": { + "strings": true + }, + "editor.detectIndentation": false, + "editor.tabSize": 2 +} diff --git a/Dockerfile b/Dockerfile index ed0be492d..7d7985b94 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,50 @@ -FROM ubuntu:latest +# ------------------------------------------------------------------- +# Minimal dockerfile from alpine base +# +# Instructions: +# ============= +# 1. Create an empty directory and copy this file into it. +# +# 2. Create image with: +# docker build --tag timeoff:latest . +# +# 3. Run with: +# docker run -d -p 3000:3000 --name alpine_timeoff timeoff +# +# -------------------------------------------------------------------- +FROM node:16-alpine -RUN apt-get update -y && apt-get upgrade -y -RUN apt-get install -y git npm nodejs nodejs-legacy +RUN apk update +#RUN apk upgrade -RUN adduser --system app --home /app -USER app +# Install dependencies +RUN apk add \ + git \ + make \ + python3 \ + g++ \ + gcc \ + libc-dev \ + clang WORKDIR /app -RUN git clone https://github.com/timeoff-management/application.git timeoff-management -WORKDIR /app/timeoff-management +LABEL org.label-schema.schema-version="1.0" +LABEL org.label-schema.docker.cmd="docker run -d -p 3000:3000 --name alpine_timeoff" + +# Cache the docker layer with the node_modules +COPY package.json /app/package.json +COPY package-lock.json /app/package-lock.json + RUN npm install +# Copy the application into the container. +COPY . /app + +# Add user so it doesn't run as root +RUN adduser --system app --home /app +USER app + EXPOSE 3000 + CMD npm start diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 000000000..cc0b71afb --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,6 @@ +FROM node:14 +WORKDIR /usr/src/app +COPY package*.json ./ +RUN npm install +COPY . . +CMD [ "node", "--inspect=0.0.0.0", "bin/wwww" ] \ No newline at end of file diff --git a/README.md b/README.md index 1233c569a..3d8a12548 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ - # TimeOff.Management -Web application for managing employees absence. - -[![Stories in Ready](https://badge.waffle.io/timeoff-management/application.png?label=ready&title=Ready)](https://waffle.io/timeoff-management/application) +Web application for managing employee absences. -Build status +Build status ## Features -**Multiple views of staff absence** +**Multiple views of staff absences** Calendar view, Team view, or Just plain list. @@ -17,62 +14,56 @@ Calendar view, Team view, or Just plain list. Add custom absence types: Sickness, Maternity, Working from home, Birthday etc. Define if each uses vacation allowance. -Optionally limit the amount of days employee can take for each Leave type. E.g. no more than 10 days of Sick days per year. +Optionally limit the amount of days employees can take for each Leave type. E.g. no more than 10 Sick days per year. Setup public holidays as well as company specific days off. -Group employees by departments: bring your organisation structure into, set the supervisor for every department. +Group employees by departments: bring your organisational structure, set the supervisor for every department. Customisable working schedule for company and individuals. **Third Party Calendar Integration** -Broadcast employees whereabout into external calendar providers: MS Outlook, Google Calendar, and iCal. +Broadcast employee whereabouts into external calendar providers: MS Outlook, Google Calendar, and iCal. -Create calendar feeds for individual, departments or entire company. +Create calendar feeds for individuals, departments or entire company. **Three Steps Workflow** -Employee request time off or revoke existing one. +Employee requests time off or revokes existing one. -Supervisor get email notification and decide about upcoming employee absence. +Supervisor gets email notification and decides about upcoming employee absence. Absence is accounted. Peers are informed via team view or calendar feeds. -**Accesss control** +**Access control** There are following types of users: employees, supervisors, and administrators. -Optional LDAP authentification: configure applicationto use your LDAP server for user authentication. - -**Seamless data migration betweeen different installations** - -User friendly and simple work-flow for data migration between different TimeOff.Management installations. +Optional LDAP authentication: configure application to use your LDAP server for user authentication. -Admin user can download the entire company data as a single JSON file. +**Ability to extract leave data into CSV** -And then restore the account at different installation by simply uploading the JSON. +Ability to back up entire company leave data into CSV file. So it could be used in any spreadsheet applications. **Works on mobile phones** The most used customer paths are mobile friendly: -* employee is able to request new leave from mobile device +- employee is able to request new leave from mobile device -* supervisor is able to record decision from the mobile as well. +- supervisor is able to record decision from the mobile as well. **Lots of other little things that would make life easier** -Manually adjust employees allowance +Manually adjust employee allowances e.g. employee has extra day in lieu. -Upon creation employee receives pro rata vacation allowance, depending on start date. - -Users of three types: admins, supervisors, and employees. +Upon creation employee receives pro-rated vacation allowance, depending on start date. Email notification to all involved parties. -Optionally allow employees to see the time off information of entire company regardless the department structure. +Optionally allow employees to see the time off information of entire company regardless of department structure. ## Screenshots @@ -88,31 +79,176 @@ Create company account and use cloud based version. ### Self hosting +```bash +# Clone the repository +git clone https://github.com/timeoff-management/application.git timeoff-management +cd timeoff-management +``` + +Edit the configuration inside the `config` folder. + +#### Standalone + Install TimeOff.Management application within your infrastructure: -(make sure you have Node.js and SQLite installed) +(make sure you have Node.js (>=16.0.0) and SQLite installed) ```bash -git clone https://github.com/timeoff-management/application.git timeoff-management -cd timeoff-management npm install npm start ``` + Open http://localhost:3000/ in your browser. +#### Using Docker + +##### Using an image from DockerHub [aliengen/timeoff-management-application](https://hub.docker.com/r/aliengen/timeoff-management-application) + +```bash +# Pull the image +docker pull aliengen/timeoff-management-application:master + +# Run the container using an `env` file to load the configuration +docker run -d -p 3000:3000 --env-file ./env --name timeoff aliengen/timeoff-management-application:master + +# Or you can run the container using the `app.json` file to load the configuration +docker run -d -p 3000:3000 -v ./config/app.json:/app/config/app.json --name timeoff aliengen/timeoff-management-application:master +``` + +##### Build the image manually + +You can build the image from the latest version of the repository. + +```bash +# Build the docker image +docker build --tag timeoff:latest . + +# Launch the docker image +docker run -d -p 3000:3000 --name alpine_timeoff timeoff +``` + +##### Using Docker-compose + +```bash +docker-compose up +``` + +## Configuration + +### Using environment variables + +| Variable | Description | Default value | +| ------------------------- | ------------------------------------------------------------ | --------------- | +| BRANDING_URL | URL of the application | http://app.timeoff.management/ | +| BRANDING_WEBSITE | URL of the company's website | http://timeoff.management/ | +| PORT | Port of the application | 3000 | +| NODE_ENV | Environment of NodeJs | development | +| CRYPTO_SECRET | Secret for password hashing | | +| DATABASE_URL | Database URL format (`mysql://localhost:3306/database`) | | +| DB_HOST | Database hostname | | +| DB_DATABASE | Database name | | +| DB_USERNAME | Database username | | +| DB_PASSWORD | Database password | | +| DB_DIALECT | Database dialect (sqlite, mysql, postgres) | sqlite | +| DB_STORAGE | Database storage file | db.[ENV].sqlite | +| DB_LOGGING | Logging of queries | false | +| DB_POOL_MAX | Maximum number of connection in pool | 5 | +| DB_POOL_MIN | Minimum number of connection in pool | 0 | +| DB_POOL_ACQUIRE | The maximum time, in milliseconds, that pool will try to get connection before throwing error | 60000 | +| DB_POOL_IDLE | The maximum time, in milliseconds, that a connection can be idle before being released. | 10000 | +| SMTP_HOST | Host of the smtp server | localhost | +| SMTP_PORT | Port of the smtp server | 25 | +| SMTP_FROM | Sender email | email@test.com | +| SMTP_AUTH_REQUIRED | Sets whether the SMTP server requires authentication | true | +| SMTP_AUTH_USER | Username for the smtp server | | +| SMTP_AUTH_PASS | Password for the smtp server | | +| SMTP_REQUIRE_TLS | Use STARTTLS | false | +| SESSIONS_SECRET | Secret for the sessions | | +| SESSIONS_STORE | Storage for the sessions (`sequelize`,`redis`) | sequelize | +| SESSIONS_REDIS_HOST | Redis hostname | localhost | +| SESSIONS_REDIS_PORT | Redis port | 6379 | +| SLACK_TOKEN | If set, the Slack token to send message | | +| SLACK_BOT_NAME | Name of the bot on Slack | | +| SLACK_ICON_URL | Icon of the bot on Slack | | +| LOGIN_DEFAULT | Display the default login form | true | +| LOGIN_GOOGLE | Enable the authentication with Google | false | +| GOOGLE_ANALYTICS_TRACKER | Google Analytics tracker code | | +| GOOGLE_AUTH_CLIENTID | Google Auth client ID | | +| GOOGLE_AUTH_CLIENTSECRET | Google Auth client secret | | +| GOOGLE_AUTH_DOMAINS | Allowed domains | | +| OPTIONS_REGISTRATION | Allow users to create a new company account on Timeoff | true | + +### Using the JSON configuration files + +#### app.json + +```json +{ + "branding": { + "url": "http://app.timeoff.management", + "website": "http://timeoff.management" + }, + "crypto_secret": "Secret used for password hashing", + "login": { + "default": true, + "google": false + }, + "smtp": { + "host": "localhost", + "port": 25, + "from": "email@test.com", + "auth": { + "user": "user", + "pass": "pass" + } + }, + "sessions": { + "secret": "Secret used for sessions", + "store": "sequelize", + "redis": { + "host": "localhost", + "port": 6379 + } + }, + "google": { + "analytics": { + "tracker": "Your GA tracker code" + }, + "auth": { + "clientId": "123", + "clientSecret": "123", + "domains": ["myalloweddomain.com"] + } + }, + "slack": { + "token": "Get your Web API token from you Slack admin page.", + "icon_url": "The image can be hosted anywhere, but I would recoment to upload an icon to your Slack and use it's url.", + "bot_name": "The display name for the messages being sent." + }, + "options": { + "registration": true + }, +} +``` + + ## Run tests We have quite a wide test coverage, to make sure that the main user paths work as expected. Please run them frequently while developing the project. +Make sure you have Chrome driver installed in your path and Chrome browser for your platform. + +If you want to see the browser execute the interactions prefix with `SHOW_CHROME=1` + ```bash -npm test +USE_CHROME=1 npm test ``` (make sure that application with default settings is up and running) -Any bugfixes or enhancements should have good test coverage to get them into "master" branch. +Any bug fixes or enhancements should have good test coverage to get them into "master" branch. ## Updating existing instance with new code @@ -126,6 +262,37 @@ npm run-script db-update npm start ``` +## How to? + +There are some customizations available. + +## How to amend or extend colours available for colour picker? +Follow instructions on [this page](docs/extend_colors_for_leave_type.md). + +## Customization + +There are few options to configure an installation. + +### Make sorting sensitive to particular locale + +Given the software could be installed for company with employees with non-English names there might be a need to +respect the alphabet while sorting customer entered content. + +For that purpose the application config file has `locale_code_for_sorting` entry. +By default the value is `en` (English). One can override it with other locales such as `cs`, `fr`, `de` etc. + +### Force employees to pick type each time new leave is booked + +Some organizations require employees to explicitly pick the type of leave when booking time off. So employee makes a choice rather than relying on default settings. +That reduce number of "mistaken" leaves, which are cancelled after. + +In order to force employee to explicitly pick the leave type of the booked time off, change `is_force_to_explicitly_select_type_when_requesting_new_leave` +flag to be `true` in the `config/app.json` file. + +## Use Redis as a sessions storage + +Follow instructions on [this page](docs/SessionStoreInRedis.md). + ## Feedback Please report any issues or feedback to twitter or Email: pavlo at timeoff.management diff --git a/app.js b/app.js index 611e37d46..863dddc45 100644 --- a/app.js +++ b/app.js @@ -1,159 +1,218 @@ +const express = require('express') +const path = require('path') +const favicon = require('serve-favicon') +const logger = require('morgan') +const cookieParser = require('cookie-parser') +const bodyParser = require('body-parser') +const moment = require('moment') +const _handlebars = require('handlebars') +const { + allowInsecurePrototypeAccess +} = require('@handlebars/allow-prototype-access') +require('dotenv').config() + +const i18n = require('./i18n') + +const app = express() + +// Handlebars +// +// Secure if only developers have access to the templates +// https://stackoverflow.com/questions/59690923/handlebars-access-has-been-denied-to-resolve-the-property-from-because-it-is -var express = require('express'); -var path = require('path'); -var favicon = require('serve-favicon'); -var logger = require('morgan'); -var cookieParser = require('cookie-parser'); -var bodyParser = require('body-parser'); -var moment = require('moment'); - -var app = express(); +// helpers +const baseHelpers = require('./lib/view/helpers')() // View engine setup -var handlebars = require('express-handlebars') - .create({ - defaultLayout : 'main', - extname : '.hbs', - helpers : require('./lib/view/helpers')(), - }); - -app.engine('.hbs', handlebars.engine); -app.set('view engine', '.hbs'); +const handlebars = require('express-handlebars').create({ + defaultLayout: 'main', + extname: '.hbs', + helpers: { + ...baseHelpers, + __: function() { + return i18n.__.apply(this, arguments) + }, + __n: function() { + return i18n.__n.apply(this, arguments) + } + }, + handlebars: allowInsecurePrototypeAccess(_handlebars), + runtimeOptions: { + allowedProtoProperties: { + full_name: true, + name: true, + get_leave_type_name: true, + get_start_leave_day: true, + get_end_leave_day: true, + get_end_leave_day: true, + ldap_auth_enabled: true, + get_reset_password_token: true + }, + allowedProtoMethods: { + full_name: true, + name: true, + get_leave_type_name: true, + get_start_leave_day: true, + get_end_leave_day: true, + get_end_leave_day: true, + ldap_auth_enabled: true, + get_reset_password_token: true + } + } +}) + +// Language change route +app.get('/lang/:locale', (req, res) => { + res.cookie('lang', req.params.locale, { maxAge: 900000, httpOnly: true }) + res.redirect('back') +}) + +app.engine('.hbs', handlebars.engine) +app.set('view engine', '.hbs') // Add single reference to the model into application object // and reuse it whenever an access to DB is needed -app.set('db_model', require('./lib/model/db')); +app.set('db_model', require('./lib/model/db')) // uncomment after placing your favicon in /public -//app.use(favicon(__dirname + '/public/favicon.ico')); -app.use(logger('dev')); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: false })); -app.use(cookieParser()); -app.use(express.static(path.join(__dirname, 'public'))); - - +// app.use(favicon(__dirname + '/public/favicon.ico')); +app.use(logger('dev')) +app.use(bodyParser.json()) +app.use(bodyParser.urlencoded({ extended: false })) +app.use(cookieParser()) +app.use(express.static(path.join(__dirname, 'public'))) // Setup authentication mechanism -var passport = require('./lib/passport')(); +const passport = require('./lib/passport')() -var session = require('express-session'); +const session = require('express-session') // initalize sequelize with session store -var SequelizeStore = require('connect-session-sequelize')(session.Store); -app.use(session({ - secret : 'my dirty secret ;khjsdkjahsdajhasdam,nnsnad,', - resave : false, - saveUninitialized : false, +const SequelizeStore = require('connect-session-sequelize')(session.Store) +app.use( + session({ + secret: 'my dirty secret ;khjsdkjahsdajhasdam,nnsnad,', + resave: false, + saveUninitialized: false, store: new SequelizeStore({ db: app.get('db_model').sequelize - }), -})) -app.use(passport.initialize()); -app.use(passport.session()); - - + }) + }) +) +app.use(passport.initialize()) +app.use(passport.session()) // Custom middlewares // // Make sure session and user objects are available in templates -app.use(function(req,res,next){ - res.locals.session = req.session; - res.locals.logged_user = req.user; - res.locals.url_to_the_site_root = '/'; - res.locals.requested_path = req.originalUrl; - // For book leave request modal - res.locals.booking_start = moment(); - res.locals.booking_end = moment(); - next(); -}); - -app.use(function(req,res,next){ - res.locals.custom_java_script = [ - '/js/bootstrap-datepicker.js', - '/js/global.js' - ]; - res.locals.custom_css = [ - '/css/bootstrap-datepicker3.standalone.css' - ]; - - next(); -}); +app.use(function(req, res, next) { + // Get today given user's timezone + let today + + if (req.user && req.user.company) { + today = req.user.company.get_today() + } else { + today = moment.utc() + } + + res.locals.session = req.session + res.locals.logged_user = req.user + res.locals.url_to_the_site_root = '/' + // set header from env or default to static string + res.locals.header_title = process.env.HEADER_TITLE || 'Time Off Requests' + // set branding url + res.locals.branding_url = process.env.BRANDING_URL || 'https://timeoff.com' + // For book leave request modal + res.locals.booking_start = today + res.locals.booking_end = today + res.locals.keep_team_view_hidden = !!( + req.user && + req.user.company.is_team_view_hidden && + !req.user.admin + ) + + next() +}) + +app.use(function(_req, res, next) { + res.locals.custom_java_script = [ + '/js/bootstrap-datepicker.js', + '/js/global.js' + ] + res.locals.custom_css = ['/css/bootstrap-datepicker3.standalone.css'] + next() +}) // Enable flash messages within session -app.use( require('./lib/middleware/flash_messages') ); +app.use(require('./lib/middleware/flash_messages')) -app.use( require('./lib/middleware/session_aware_redirect') ); +app.use(require('./lib/middleware/session_aware_redirect')) // Here will be publicly accessible routes -app.use( - '/feed/', - require('./lib/route/feed') -); +app.use('/feed/', require('./lib/route/feed')) + +app.use('/integration/v1/', require('./lib/route/integration_api')(passport)) app.use( '/', require('./lib/route/login')(passport), // All rotes bellow are only for authenticated users - require('./lib/route/dashboard.js') -); + require('./lib/route/dashboard') +) -app.use( - '/calendar/', - require('./lib/route/calendar.js') -); +app.use('/api/v1/', require('./lib/route/api')) -app.use( - '/settings/', - require('./lib/route/settings.js') -); +app.use('/calendar/', require('./lib/route/calendar')) + +app.use('/settings/', require('./lib/route/settings')) + +// '/settings/' path is quite big hence there are two modules providing handlers for it +app.use('/settings/', require('./lib/route/departments')) +app.use('/settings/', require('./lib/route/bankHolidays')) app.use( '/users/', - require('./lib/route/users.js') -); + // Order of following requires for /users/ matters + require('./lib/route/users/summary'), + require('./lib/route/users') +) -app.use( - '/requests/', - require('./lib/route/requests.js') -); +app.use('/requests/', require('./lib/route/requests')) -app.use( - '/audit/', - require('./lib/route/audit.js') -); +app.use('/audit/', require('./lib/route/audit')) -// catch 404 and forward to error handler -app.use(function(req, res, next) { - var err = new Error('Not Found'); - err.status = 404; - next(err); -}); +app.use('/reports/', require('./lib/route/reports')) +// catch 404 +app.use(function(req, res, next) { + res.render('not_found') +}) // error handlers // development error handler // will print stacktrace if (app.get('env') === 'development') { - app.use(function(err, req, res, next) { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: err - }); - }); + app.use(function(err, req, res, next) { + console.error(err) + res.status(err.status || 500) + res.render('error', { + message: err.message, + error: err + }) + }) } // production error handler // no stacktraces leaked to user app.use(function(err, req, res, next) { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: {} - }); -}); - -module.exports = app; + console.error(err) + res.status(err.status || 500) + res.render('error', { + message: err.message, + error: {} + }) +}) + +module.exports = app diff --git a/bin/calculate_carry_over_allowance_for_all_users.js b/bin/calculate_carry_over_allowance_for_all_users.js new file mode 100644 index 000000000..24608c2a0 --- /dev/null +++ b/bin/calculate_carry_over_allowance_for_all_users.js @@ -0,0 +1,15 @@ +'use strict' + +const { + calculateCarryOverAllowance +} = require('../lib/model/calculateCarryOverAllowance') +const models = require('../lib/model/db') + +models.User.findAll() + .then(users => calculateCarryOverAllowance({ users })) + .then(() => console.log('Done!')) + .catch(error => + console.log( + `Failed to recalculate carry over allowance: ${error} at ${error.stack}` + ) + ) diff --git a/bin/check_where_i_am_on_google.js b/bin/check_where_i_am_on_google.js index 028a82f19..65a8a6053 100644 --- a/bin/check_where_i_am_on_google.js +++ b/bin/check_where_i_am_on_google.js @@ -1,66 +1,106 @@ - // Usage: // node bin/check_where_i_am_on_google.js --domain="nixieshop.com" --query="nixie shop" -'use strict'; - -var - optimist = require('optimist').argv, - search_query = optimist.query || 'time off manager', - web_site_domain = optimist.domain || 'timeoffmanager.com', - stop_on_first = true; - -console.log('-----------------------------------'); -console.log(' Cheking query string : '+search_query); -console.log(' Look for domain : '+web_site_domain); -console.log('-----------------------------------'); +'use strict' +const optimist = require('optimist').argv +const search_query = optimist.query || 'time off manager' +const web_site_domain = optimist.domain || 'timeoffmanager.com' +const stop_on_first = true -var webdriver = require('selenium-webdriver'), - _ = require('underscore'), - google_url = 'https://www.google.com/search?q='+search_query+'&start=', - driver; +console.log('-----------------------------------') +console.log(' Cheking query string : ' + search_query) +console.log(' Look for domain : ' + web_site_domain) +console.log('-----------------------------------') +const webdriver = require('selenium-webdriver') +const _ = require('underscore') +const google_url = 'https://www.google.com/search?q=' + search_query + '&start=' +let driver // Instantiate new driver object driver = new webdriver.Builder() .withCapabilities(webdriver.Capabilities.chrome()) - .build(); - - - -_.map([ - 0,10,20,30,40,50,60,70,80,90,100, - 110,120,130,140,150,160,170,180,190,200, - 210,220,230,240,250,260,270,280,290,300, - 310,320,330,340,350,360,370,380,390,400, - 410,420,430,440,450,460,470,480,490,500, -// 510,520,530,540,550,560,570,580,590,600, -// 610,620,630,640,650,660,670,680,690,700, -// 710,720,730,740,750,760,770,780,790,800, -// 810,820,830,840,850,860,870,880,890,900, -// 910,920,930,940,950,960,970,980,990,1000, -], function(i){ - - driver.sleep(500); + .build() + +_.map( + [ + 0, + 10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90, + 100, + 110, + 120, + 130, + 140, + 150, + 160, + 170, + 180, + 190, + 200, + 210, + 220, + 230, + 240, + 250, + 260, + 270, + 280, + 290, + 300, + 310, + 320, + 330, + 340, + 350, + 360, + 370, + 380, + 390, + 400, + 410, + 420, + 430, + 440, + 450, + 460, + 470, + 480, + 490, + 500 + // 510,520,530,540,550,560,570,580,590,600, + // 610,620,630,640,650,660,670,680,690,700, + // 710,720,730,740,750,760,770,780,790,800, + // 810,820,830,840,850,860,870,880,890,900, + // 910,920,930,940,950,960,970,980,990,1000, + ], + function(i) { + driver.sleep(500) // Go to front page - driver.get( google_url + i ); - - driver.getPageSource().then(function(source){ - if (source.match(new RegExp('http(s)?://(www.)?'+web_site_domain))) { - console.log('>>>>>>> Found on '+(1+i/10)+' page'); - if (stop_on_first) { - driver.quit().then(function(){ - process.exit(); - }); - } - } else { - console.log((1+i/10)+'...'); + driver.get(google_url + i) + + driver.getPageSource().then(function(source) { + if (source.match(new RegExp('http(s)?://(www.)?' + web_site_domain))) { + console.log('>>>>>>> Found on ' + (1 + i / 10) + ' page') + if (stop_on_first) { + driver.quit().then(function() { + process.exit() + }) } - }); -}); - - -driver.quit(); - + } else { + console.log(1 + i / 10 + '...') + } + }) + } +) + +driver.quit() diff --git a/bin/fetch_user_stat.js b/bin/fetch_user_stat.js new file mode 100644 index 000000000..3c38f1cf2 --- /dev/null +++ b/bin/fetch_user_stat.js @@ -0,0 +1,110 @@ +'use strict' + +const By = require('selenium-webdriver').By +const Promise = require('bluebird') +const register_new_user_func = require('../t/lib/register_new_user') +const login_user_func = require('../t/lib/login_with_user') +const logout_user_func = require('../t/lib/logout_user') +const open_page_func = require('../t/lib/open_page') +const config = require('../t/lib/config') +const application_host = config.get_application_host() + +/* + * THis is simple scrip to execute on different versions of application + * to check if new version introduces any anomalies. + * + * Example of running: + * + * node node_modules/mocha/bin/mocha --recursive bin/fetch_user_stat.js + * + * */ + +describe('Collect remaining days for employees', function() { + this.timeout(config.get_execution_timeout()) + + const report = {} + let driver + + it('Create new company', function(done) { + register_new_user_func({ + application_host + }).then(function(data) { + driver = data.driver + done() + }) + }) + + it('Logout', function(done) { + logout_user_func({ + application_host, + driver + }).then(function() { + done() + }) + }) + + // This is a list of accountes to iterate through + // By default it is dummy ones + const users = ['test@test.com', 'test2@test.com'] + + for (const email of users) { + it('Login as user', function(done) { + login_user_func({ + application_host, + user_email: email, + driver + }).then(function() { + done() + }) + }) + + it('Open users page', function(done) { + open_page_func({ + url: application_host + 'users/', + driver + }).then(function() { + done() + }) + }) + + it('Fetch remaining days for each employee', function(done) { + driver + .findElements(By.css('tr[data-vpp-user-row]')) + + .then(els => + Promise.map( + els, + el => { + let user_id + + return el + .getAttribute('data-vpp-user-row') + .then(u_id => Promise.resolve((user_id = u_id))) + .then(() => el.findElement(By.css('td.vpp-days-remaining'))) + .then(el => el.getText()) + .then(days => Promise.resolve((report[user_id] = days))) + }, + { concurrency: 0 } + ) + ) + + .then(() => done()) + }) + + it('Logout user', function(done) { + logout_user_func({ + application_host, + driver + }).then(function() { + done() + }) + }) + } + + after(function(done) { + console.dir(report) + driver.quit().then(function() { + done() + }) + }) +}) diff --git a/bin/wwww b/bin/wwww index 2caaa0a8c..1fc786667 100755 --- a/bin/wwww +++ b/bin/wwww @@ -1,12 +1,15 @@ #!/usr/bin/env node -var debug = require('debug')('vacation-tracker'); -var app = require('../app'); +const debug = require('debug')('vacation-tracker') +const app = require('../app') -app.set('port', process.env.PORT || 3000); +app.set('port', process.env.PORT || 3000) -app.get('db_model').sequelize.sync().then(function () { - var server = app.listen(app.get('port'), function() { - debug('Express server listening on port ' + server.address().port); - }); -}); +app + .get('db_model') + .sequelize.sync() + .then(function() { + var server = app.listen(app.get('port'), function() { + debug('Express server listening on port ' + server.address().port) + }) + }) diff --git a/bin/wwww_cluster b/bin/wwww_cluster index 039fc8f6c..b4afea0b0 100644 --- a/bin/wwww_cluster +++ b/bin/wwww_cluster @@ -1,28 +1,30 @@ #!/usr/bin/env node -var debug = require('debug')('vacation-tracker'); -var app = require('../app'); +const debug = require('debug')('vacation-tracker') +const app = require('../app') -var cluster = require('cluster'); +const cluster = require('cluster') -if (cluster.isMaster ) { - - cluster.on('exit', function(worker, code){ - if(code != 0 && !worker.suicide) { - console.log('>>>>>> Worker crashed. Starting a new worker'); - cluster.fork(); +if (cluster.isMaster) { + cluster.on('exit', function(worker, code) { + if (code != 0 && !worker.suicide) { + console.log('>>>>>> Worker crashed. Starting a new worker') + cluster.fork() } - }); - - [1,2].forEach(function(){cluster.fork()}); + }) + ;[1, 2].forEach(function() { + cluster.fork() + }) } else { - app.set('port', process.env.PORT || 3000); - - app.get('db_model').sequelize.sync().then(function () { - var server = app.listen(app.get('port'), function() { - console.log('Started to listen on port ' + server.address().port); - }); - }); - + app.set('port', process.env.PORT || 3000) + + app + .get('db_model') + .sequelize.sync() + .then(function() { + var server = app.listen(app.get('port'), function() { + console.log('Started to listen on port ' + server.address().port) + }) + }) } diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 000000000..4ebf18785 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Update and install necessary packages +sudo apt-get update +sudo apt-get install -y tmux ranger tig cargo mycli postgresql-client postgresql-client-common libpq-dev + +# install lazygit +LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep -Po '"tag_name": "v\K[^"]*') +curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz" +tar xf lazygit.tar.gz lazygit +sudo install lazygit /usr/local/bin +rm lazygit.tar.gz +rm -rf lazygit + +# Remove pgcli if installed via apt +sudo apt-get remove -y pgcli + +# Ensure Python pip is installed +sudo apt-get install -y python3-pip + +# Install pgcli via pip +pip3 install pgcli + +# Clone the GitHub repository into a directory named "tmux" if it doesn't already exist +if [ ! -d "$HOME/tmux" ]; then + git clone https://github.com/gpakosz/.tmux.git "$HOME/tmux" +fi + +# Create a symbolic link for the .tmux.conf file from the cloned repo to the home directory if it doesn't exist +if [ ! -L "$HOME/.tmux.conf" ]; then + ln -s "$HOME/tmux/.tmux.conf" "$HOME/" +fi + +# Neovim installation and setup +NVIM_VERSION="v0.9.5" +DOWNLOAD_URL="https://github.com/neovim/neovim/releases/download/${NVIM_VERSION}/nvim-linux64.tar.gz" +DOWNLOAD_DIR="$HOME/Downloads" +NVIM_TAR="$DOWNLOAD_DIR/nvim.tar.gz" +EXTRACT_DIR="$HOME/nvim" +NVIM_BIN="$EXTRACT_DIR/bin" + +# Step 1: Download Neovim only if it doesn't exist or version is different +if [ ! -d "$NVIM_BIN" ] || [ ! -f "$NVIM_BIN/nvim" ] || [ "$($NVIM_BIN/nvim --version | grep -o "$NVIM_VERSION")" != "$NVIM_VERSION" ]; then + mkdir -p "$DOWNLOAD_DIR" + curl -L -o "$NVIM_TAR" "$DOWNLOAD_URL" + + # Step 2: Extract Neovim + mkdir -p "$EXTRACT_DIR" + tar -xzf "$NVIM_TAR" -C "$EXTRACT_DIR" --strip-components 1 +fi + +# Step 3: Add Neovim bin to PATH if not already added +if ! grep -q "$NVIM_BIN" "$HOME/.bashrc"; then + echo "export PATH=\$PATH:$NVIM_BIN" >>"$HOME/.bashrc" +fi + +# Reload .bashrc to apply changes immediately +source "$HOME/.bashrc" + +# Step 4: Install Neovim plugins if not already installed +if [ ! -d "$HOME/.config/nvim" ]; then + git clone https://github.com/LazyVim/starter "$HOME/.config/nvim" + rm -rf "$HOME/.config/nvim/.git" +fi + +echo "Neovim has been installed and added to PATH. Please restart your terminal or source your .bashrc to apply changes." + +# Function to check and install Cargo packages +check_and_install() { + if ! cargo install --list | grep -q "^$1 "; then + echo "Installing $1..." + if [ -z "$2" ]; then + cargo install --locked --force "$1" + else + cargo install --locked --force "$1" --version "$2" + fi + else + echo "$1 is already installed." + fi +} + +# Utilizing the function for each package +# check_and_install cargo-binstall +# check_and_install cargo-shuttle + +# Add aliases if they don't exist +declare -a aliases=( + 'alias dcu="docker compose up -d"' + 'alias dcd="docker compose down"' + 'alias dlf="docker logs -f"' + 'alias dls="watch docker ps -a"' + 'alias up="cd ../"' + 'alias dreset="dcd && yes | docker system prune -a && dcu --build"' + 'alias glg="git log --oneline --decorate --graph --all"' +) + +for alias_cmd in "${aliases[@]}"; do + alias_name=$(echo "$alias_cmd" | awk -F'=' '{print $1}' | awk '{print $2}') + + if ! grep -q "^alias $alias_name=" "$HOME/.bashrc"; then + echo "$alias_cmd" >>"$HOME/.bashrc" + echo "Added $alias_name to ~/.bashrc" + else + echo "$alias_name already exists in ~/.bashrc" + fi +done + +# Set EDITOR to nvim if not already set +if ! grep -q "EDITOR=nvim" "$HOME/.bashrc"; then + echo 'export EDITOR=nvim' >>"$HOME/.bashrc" +fi + +# Reload .bashrc to apply changes immediately +source "$HOME/.bashrc" diff --git a/config.example/app.json b/config.example/app.json new file mode 100644 index 000000000..008eca690 --- /dev/null +++ b/config.example/app.json @@ -0,0 +1,48 @@ +{ + "branding": { + "url": "http://app.timeoff.management", + "website": "http://timeoff.management" + }, + "crypto_secret": "!2~`HswpPPLa22+=±§sdq qwe,appp qwwokDF_", + "login": { + "default": true, + "google": false + }, + "smtp": { + "host": "localhost", + "port": 25, + "from": "email@test.com", + "auth": { + "user": "user", + "pass": "pass" + } + }, + "sessions": { + "secret": "my dirty secret ;khjsdkjahsdajhasdam,nnsnad,", + "store": "sequelize", + "redis": { + "host": "localhost", + "port": 6379 + } + }, + "google": { + "analytics": { + "tracker": "" + }, + "auth": { + "clientId": "123", + "clientSecret": "123", + "domains": ["mydomain.com"] + } + }, + "slack": { + "token": "Get your Web API token from you Slack admin page.", + "icon_url": "The image can be hosted anywhere, but I would recoment to upload an icon to your Slack and use it's url.", + "bot_name": "The display name for the messages being sent." + }, + "options": { + "registration": true + }, + "locale_code_for_sorting": "en", + "force_to_explicitly_select_type_when_requesting_new_leave": false +} diff --git a/config.example/config.js b/config.example/config.js new file mode 100644 index 000000000..14079b6c8 --- /dev/null +++ b/config.example/config.js @@ -0,0 +1,14 @@ +require('dotenv').config() + +module.exports = { + development: { + url: process.env.DATABASE_URL, + dialect: 'postgres', + dialectOptions: { + ssl: { + require: true, + rejectUnauthorized: false // for self-signed certificates; set to true for verified certificates + } + } + } +} diff --git a/config/db.json b/config.example/db.json similarity index 62% rename from config/db.json rename to config.example/db.json index 661fead1b..6b170c388 100644 --- a/config/db.json +++ b/config.example/db.json @@ -1,14 +1,15 @@ { "development": { - "dialect": "sqlite", - "storage": "./db.development.sqlite" + "username": "user", + "password": "pass", + "database": "dbname", + "host": "mysql", + "dialect": "mysql" }, "test": { - "username": "root", - "password": null, - "database": "database_test", - "host": "127.0.0.1", - "dialect": "mysql" + "dialect": "sqlite", + "storage": "./localdb.sqlite", + "logging": false }, "production": { "username": "root", diff --git a/config.example/localisation.json b/config.example/localisation.json new file mode 100644 index 000000000..d44b720c8 --- /dev/null +++ b/config.example/localisation.json @@ -0,0 +1,27287 @@ +{ + "countries": { + "AD": { + "name": "Andorra", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Any nou" + }, + { + "date": "2018-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-04-02", + "name": "Lunes de Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-20", + "name": "Pentecostés" + }, + { + "date": "2018-05-21", + "name": "Lunes de Pentecostés" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Nadal" + }, + { + "date": "2018-12-26", + "name": "San Esteban" + }, + { + "date": "2019-01-01", + "name": "Any nou" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-04-22", + "name": "Lunes de Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-09", + "name": "Pentecostés" + }, + { + "date": "2019-06-10", + "name": "Lunes de Pentecostés" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Nadal" + }, + { + "date": "2019-12-26", + "name": "San Esteban" + }, + { + "date": "2020-01-01", + "name": "Any nou" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-04-13", + "name": "Lunes de Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-31", + "name": "Pentecostés" + }, + { + "date": "2020-06-01", + "name": "Lunes de Pentecostés" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Nadal" + }, + { + "date": "2020-12-26", + "name": "San Esteban" + }, + { + "date": "2021-01-01", + "name": "Any nou" + }, + { + "date": "2021-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-04-05", + "name": "Lunes de Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-23", + "name": "Pentecostés" + }, + { + "date": "2021-05-24", + "name": "Lunes de Pentecostés" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Nadal" + }, + { + "date": "2021-12-26", + "name": "San Esteban" + } + ] + }, + "AG": { + "name": "Antigua & Barbuda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2018-08-02", + "name": "Last Lap" + }, + { + "date": "2018-11-01", + "name": "Independence Day" + }, + { + "date": "2018-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2019-08-02", + "name": "Last Lap" + }, + { + "date": "2019-11-01", + "name": "Independence Day" + }, + { + "date": "2019-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2020-08-02", + "name": "Last Lap" + }, + { + "date": "2020-11-02", + "name": "Independence Day" + }, + { + "date": "2020-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2021-08-02", + "name": "Last Lap" + }, + { + "date": "2021-11-01", + "name": "Independence Day" + }, + { + "date": "2021-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AI": { + "name": "Anguilla", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-05-30", + "name": "Anguilla Day" + }, + { + "date": "2018-06-11", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2018-08-02", + "name": "August Thursday" + }, + { + "date": "2018-08-03", + "name": "Constitution Day" + }, + { + "date": "2018-08-06", + "name": "August Monday" + }, + { + "date": "2018-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-30", + "name": "Anguilla Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-06-10", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2019-08-01", + "name": "August Thursday" + }, + { + "date": "2019-08-02", + "name": "Constitution Day" + }, + { + "date": "2019-08-05", + "name": "August Monday" + }, + { + "date": "2019-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-30", + "name": "Anguilla Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-08", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2020-08-03", + "name": "August Monday" + }, + { + "date": "2020-08-06", + "name": "August Thursday" + }, + { + "date": "2020-08-07", + "name": "Constitution Day" + }, + { + "date": "2020-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-05-30", + "name": "Anguilla Day" + }, + { + "date": "2021-06-14", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2021-08-02", + "name": "August Monday" + }, + { + "date": "2021-08-05", + "name": "August Thursday" + }, + { + "date": "2021-08-06", + "name": "Constitution Day" + }, + { + "date": "2021-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AL": { + "name": "Shqipëri", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Viti i Ri" + }, + { + "date": "2018-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2018-03-14", + "name": "Dita e Verës" + }, + { + "date": "2018-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2018-04-01", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-02", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-08", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-04-09", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2018-06-15", + "name": "Fitër Bajrami" + }, + { + "date": "2018-08-21", + "name": "Kurban Bajrami" + }, + { + "date": "2018-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2018-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2018-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2018-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2018-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2018-12-25", + "name": "Krishtlindja" + }, + { + "date": "2019-01-01", + "name": "Viti i Ri" + }, + { + "date": "2019-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2019-03-14", + "name": "Dita e Verës" + }, + { + "date": "2019-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2019-04-21", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-22", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-28", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-04-29", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2019-06-04", + "name": "Fitër Bajrami" + }, + { + "date": "2019-08-11", + "name": "Kurban Bajrami" + }, + { + "date": "2019-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2019-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2019-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2019-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2019-12-09", + "name": "Dita Kombëtare e Rinisë (ditë zëvendësuese)" + }, + { + "date": "2019-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2019-12-25", + "name": "Krishtlindja" + }, + { + "date": "2020-01-01", + "name": "Viti i Ri" + }, + { + "date": "2020-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2020-03-14", + "name": "Dita e Verës" + }, + { + "date": "2020-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2020-03-23", + "name": "Dita e Sulltan Nevruzit (ditë zëvendësuese)" + }, + { + "date": "2020-04-12", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-13", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-19", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-04-20", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2020-05-24", + "name": "Fitër Bajrami" + }, + { + "date": "2020-07-31", + "name": "Kurban Bajrami" + }, + { + "date": "2020-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2020-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2020-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2020-11-30", + "name": "Dita e Çlirimit (ditë zëvendësuese)" + }, + { + "date": "2020-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2020-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2020-12-25", + "name": "Krishtlindja" + }, + { + "date": "2021-01-01", + "name": "Viti i Ri" + }, + { + "date": "2021-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2021-03-14", + "name": "Dita e Verës" + }, + { + "date": "2021-03-15", + "name": "Dita e Verës (ditë zëvendësuese)" + }, + { + "date": "2021-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2021-04-04", + "name": "Pashkët Katolike" + }, + { + "date": "2021-04-05", + "name": "Pashkët Katolike" + }, + { + "date": "2021-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2021-05-02", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-03", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-13", + "name": "Fitër Bajrami" + }, + { + "date": "2021-07-20", + "name": "Kurban Bajrami" + }, + { + "date": "2021-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2021-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2021-11-29", + "name": "Dita e Pavarësisë (ditë zëvendësuese)" + }, + { + "date": "2021-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2021-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2021-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2021-12-25", + "name": "Krishtlindja" + } + ] + }, + "AM": { + "name": "Հայաստան", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ամանոր" + }, + { + "date": "2018-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2018-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2018-01-28", + "name": "Բանակի օր" + }, + { + "date": "2018-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2018-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2018-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2018-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2018-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2018-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2018-09-21", + "name": "Անկախության օր" + }, + { + "date": "2018-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2019-01-01", + "name": "Ամանոր" + }, + { + "date": "2019-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2019-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2019-01-28", + "name": "Բանակի օր" + }, + { + "date": "2019-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2019-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2019-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2019-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2019-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2019-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2019-09-21", + "name": "Անկախության օր" + }, + { + "date": "2019-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2020-01-01", + "name": "Ամանոր" + }, + { + "date": "2020-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2020-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2020-01-28", + "name": "Բանակի օր" + }, + { + "date": "2020-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2020-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2020-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2020-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2020-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2020-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2020-09-21", + "name": "Անկախության օր" + }, + { + "date": "2020-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2021-01-01", + "name": "Ամանոր" + }, + { + "date": "2021-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2021-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2021-01-28", + "name": "Բանակի օր" + }, + { + "date": "2021-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2021-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2021-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2021-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2021-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2021-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2021-09-21", + "name": "Անկախության օր" + }, + { + "date": "2021-12-31", + "name": "Նոր տարվա գիշեր" + } + ] + }, + "AO": { + "name": "Angola", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-04", + "name": "Dia da Paz" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2018-11-02", + "name": "Dia de Finados" + }, + { + "date": "2018-11-11", + "name": "Dia da Independência" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2019-04-04", + "name": "Dia da Paz" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2019-11-02", + "name": "Dia de Finados" + }, + { + "date": "2019-11-11", + "name": "Dia da Independência" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2020-04-04", + "name": "Dia da Paz" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2020-11-02", + "name": "Dia de Finados" + }, + { + "date": "2020-11-11", + "name": "Dia da Independência" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-04", + "name": "Dia da Paz" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2021-11-02", + "name": "Dia de Finados" + }, + { + "date": "2021-11-11", + "name": "Dia da Independência" + }, + { + "date": "2021-12-25", + "name": "Natal" + } + ] + }, + "AR": { + "name": "Argentina", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2018-04-30", + "name": "Feriado Puente Turístico" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2018-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2018-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-20", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2018-10-15", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2018-11-26", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2019-04-01", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2019-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2019-06-21", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-19", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2019-10-14", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2019-11-25", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-23", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-23", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2020-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2020-04-03", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2020-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2020-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-17", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2020-10-12", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2020-11-23", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2020-12-07", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-24", + "name": "Feriado Puente Turístico" + }, + { + "date": "2021-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2021-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2021-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-16", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2021-10-11", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2021-11-22", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "AS": { + "name": "American Samoa", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-09-03", + "name": "Labour Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-24", + "name": "Christmas Eve" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-31", + "name": "New Year's Eve" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-09-02", + "name": "Labour Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-24", + "name": "Christmas Eve" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-31", + "name": "New Year's Eve" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-09-07", + "name": "Labour Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-24", + "name": "Christmas Eve" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-31", + "name": "New Year's Eve" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-09-06", + "name": "Labour Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-24", + "name": "Christmas Eve" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + }, + { + "date": "2021-12-31", + "name": "New Year's Eve" + } + ] + }, + "AT": { + "name": "Österreich", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2018-04-01", + "name": "Ostersonntag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2018-05-10", + "name": "Christi Himmelfahrt" + }, + { + "date": "2018-05-20", + "name": "Pfingstsonntag" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-05-31", + "name": "Fronleichnam" + }, + { + "date": "2018-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2018-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2018-11-01", + "name": "Allerheiligen" + }, + { + "date": "2018-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "Christtag" + }, + { + "date": "2018-12-26", + "name": "Stefanitag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2019-04-21", + "name": "Ostersonntag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2019-05-30", + "name": "Christi Himmelfahrt" + }, + { + "date": "2019-06-09", + "name": "Pfingstsonntag" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-06-20", + "name": "Fronleichnam" + }, + { + "date": "2019-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2019-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2019-11-01", + "name": "Allerheiligen" + }, + { + "date": "2019-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "Christtag" + }, + { + "date": "2019-12-26", + "name": "Stefanitag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2020-04-12", + "name": "Ostersonntag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2020-05-21", + "name": "Christi Himmelfahrt" + }, + { + "date": "2020-05-31", + "name": "Pfingstsonntag" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-06-11", + "name": "Fronleichnam" + }, + { + "date": "2020-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2020-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2020-11-01", + "name": "Allerheiligen" + }, + { + "date": "2020-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "Christtag" + }, + { + "date": "2020-12-26", + "name": "Stefanitag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2021-04-04", + "name": "Ostersonntag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2021-05-13", + "name": "Christi Himmelfahrt" + }, + { + "date": "2021-05-23", + "name": "Pfingstsonntag" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-06-03", + "name": "Fronleichnam" + }, + { + "date": "2021-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2021-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2021-11-01", + "name": "Allerheiligen" + }, + { + "date": "2021-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "Christtag" + }, + { + "date": "2021-12-26", + "name": "Stefanitag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "AU": { + "name": "Australia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Australia Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-25", + "name": "ANZAC Day" + }, + { + "date": "2018-06-11", + "name": "Queen's Birthday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Australia Day" + }, + { + "date": "2019-01-28", + "name": "Australia Day (substitute day)" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "ANZAC Day" + }, + { + "date": "2019-06-10", + "name": "Queen's Birthday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-26", + "name": "Australia Day" + }, + { + "date": "2020-01-27", + "name": "Australia Day (substitute day)" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-25", + "name": "ANZAC Day" + }, + { + "date": "2020-04-27", + "name": "ANZAC Day (substitute day)" + }, + { + "date": "2020-06-08", + "name": "Queen's Birthday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Australia Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-25", + "name": "ANZAC Day" + }, + { + "date": "2021-04-26", + "name": "ANZAC Day (substitute day)" + }, + { + "date": "2021-06-14", + "name": "Queen's Birthday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AW": { + "name": "Aruba", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Aña Nobo" + }, + { + "date": "2018-01-25", + "name": "Dia di Betico" + }, + { + "date": "2018-02-12", + "name": "Dialuna di Carnaval" + }, + { + "date": "2018-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2018-03-19", + "name": "Dia di Himno y Bandera (substituut)" + }, + { + "date": "2018-03-30", + "name": "Diabierna Santo" + }, + { + "date": "2018-04-02", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2018-04-27", + "name": "Aña di Rey" + }, + { + "date": "2018-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2018-05-10", + "name": "Dia di Asuncion" + }, + { + "date": "2018-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2018-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2019-01-01", + "name": "Aña Nobo" + }, + { + "date": "2019-01-25", + "name": "Dia di Betico" + }, + { + "date": "2019-03-04", + "name": "Dialuna di Carnaval" + }, + { + "date": "2019-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2019-04-19", + "name": "Diabierna Santo" + }, + { + "date": "2019-04-22", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2019-04-27", + "name": "Aña di Rey" + }, + { + "date": "2019-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2019-05-30", + "name": "Dia di Asuncion" + }, + { + "date": "2019-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2019-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2020-01-01", + "name": "Aña Nobo" + }, + { + "date": "2020-01-25", + "name": "Dia di Betico" + }, + { + "date": "2020-02-24", + "name": "Dialuna di Carnaval" + }, + { + "date": "2020-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2020-04-10", + "name": "Diabierna Santo" + }, + { + "date": "2020-04-13", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2020-04-27", + "name": "Aña di Rey" + }, + { + "date": "2020-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2020-05-21", + "name": "Dia di Asuncion" + }, + { + "date": "2020-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2020-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2021-01-01", + "name": "Aña Nobo" + }, + { + "date": "2021-01-25", + "name": "Dia di Betico" + }, + { + "date": "2021-02-15", + "name": "Dialuna di Carnaval" + }, + { + "date": "2021-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2021-04-02", + "name": "Diabierna Santo" + }, + { + "date": "2021-04-05", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2021-04-27", + "name": "Aña di Rey" + }, + { + "date": "2021-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2021-05-13", + "name": "Dia di Asuncion" + }, + { + "date": "2021-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2021-12-26", + "name": "Di dos Dia Pasco di Nascimento" + } + ] + }, + "AX": { + "name": "Landskapet Åland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2018-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2018-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2018-03-30", + "name": "Långfredagen" + }, + { + "date": "2018-04-02", + "name": "Annandag påsk" + }, + { + "date": "2018-05-01", + "name": "Första Maj" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2018-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2018-06-15", + "name": "Midsommarafton" + }, + { + "date": "2018-06-16", + "name": "Midsommardagen" + }, + { + "date": "2018-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2018-12-24", + "name": "Julafton" + }, + { + "date": "2018-12-25", + "name": "Juldagen" + }, + { + "date": "2018-12-26", + "name": "Annandag jul" + }, + { + "date": "2018-12-31", + "name": "Nyårsafton" + }, + { + "date": "2019-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2019-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2019-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2019-04-19", + "name": "Långfredagen" + }, + { + "date": "2019-04-22", + "name": "Annandag påsk" + }, + { + "date": "2019-05-01", + "name": "Första Maj" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2019-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2019-06-15", + "name": "Midsommardagen" + }, + { + "date": "2019-06-21", + "name": "Midsommarafton" + }, + { + "date": "2019-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2019-12-24", + "name": "Julafton" + }, + { + "date": "2019-12-25", + "name": "Juldagen" + }, + { + "date": "2019-12-26", + "name": "Annandag jul" + }, + { + "date": "2019-12-31", + "name": "Nyårsafton" + }, + { + "date": "2020-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2020-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2020-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2020-04-10", + "name": "Långfredagen" + }, + { + "date": "2020-04-13", + "name": "Annandag påsk" + }, + { + "date": "2020-05-01", + "name": "Första Maj" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2020-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2020-06-19", + "name": "Midsommarafton" + }, + { + "date": "2020-06-20", + "name": "Midsommardagen" + }, + { + "date": "2020-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2020-12-24", + "name": "Julafton" + }, + { + "date": "2020-12-25", + "name": "Juldagen" + }, + { + "date": "2020-12-26", + "name": "Annandag jul" + }, + { + "date": "2020-12-31", + "name": "Nyårsafton" + }, + { + "date": "2021-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2021-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2021-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2021-04-02", + "name": "Långfredagen" + }, + { + "date": "2021-04-05", + "name": "Annandag påsk" + }, + { + "date": "2021-05-01", + "name": "Första Maj" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2021-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2021-06-18", + "name": "Midsommarafton" + }, + { + "date": "2021-06-19", + "name": "Midsommardagen" + }, + { + "date": "2021-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2021-12-24", + "name": "Julafton" + }, + { + "date": "2021-12-25", + "name": "Juldagen" + }, + { + "date": "2021-12-26", + "name": "Annandag jul" + }, + { + "date": "2021-12-31", + "name": "Nyårsafton" + } + ] + }, + "AZ": { + "name": "Azərbaycan Respublikası", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Yeni il" + }, + { + "date": "2018-01-01", + "name": "Dünya azərbaycanlıların həmrəyliyi günü (əvəz gün)" + }, + { + "date": "2018-01-02", + "name": "Yeni il" + }, + { + "date": "2018-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2018-03-20", + "name": "Novruz" + }, + { + "date": "2018-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2018-05-28", + "name": "Respublika günü" + }, + { + "date": "2018-06-15", + "name": "Ramazan Bayramı" + }, + { + "date": "2018-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2018-06-16", + "name": "Ramazan Bayramı" + }, + { + "date": "2018-06-18", + "name": "Ramazan Bayramı (əvəz gün)" + }, + { + "date": "2018-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2018-08-21", + "name": "Qurban Bayramı" + }, + { + "date": "2018-08-22", + "name": "Qurban Bayramı" + }, + { + "date": "2018-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2018-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2018-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2018-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2018-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2019-01-01", + "name": "Yeni il" + }, + { + "date": "2019-01-02", + "name": "Yeni il" + }, + { + "date": "2019-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2019-03-20", + "name": "Novruz" + }, + { + "date": "2019-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2019-05-28", + "name": "Respublika günü" + }, + { + "date": "2019-06-04", + "name": "Ramazan Bayramı" + }, + { + "date": "2019-06-05", + "name": "Ramazan Bayramı" + }, + { + "date": "2019-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2019-06-17", + "name": "Azərbaycan xalqının Milli Qurtuluş günü (əvəz gün)" + }, + { + "date": "2019-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2019-08-11", + "name": "Qurban Bayramı" + }, + { + "date": "2019-08-12", + "name": "Qurban Bayramı" + }, + { + "date": "2019-08-13", + "name": "Qurban Bayramı (əvəz gün)" + }, + { + "date": "2019-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2019-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2019-11-11", + "name": "Dövlət Bayrağı günü (əvəz gün)" + }, + { + "date": "2019-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2019-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2019-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2020-01-01", + "name": "Yeni il" + }, + { + "date": "2020-01-02", + "name": "Yeni il" + }, + { + "date": "2020-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2020-03-09", + "name": "Qadınlar günü (əvəz gün)" + }, + { + "date": "2020-03-20", + "name": "Novruz" + }, + { + "date": "2020-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2020-05-11", + "name": "Faşizm üzərində qələbə günü (əvəz gün)" + }, + { + "date": "2020-05-24", + "name": "Ramazan Bayramı" + }, + { + "date": "2020-05-25", + "name": "Ramazan Bayramı" + }, + { + "date": "2020-05-26", + "name": "Ramazan Bayramı (əvəz gün)" + }, + { + "date": "2020-05-28", + "name": "Respublika günü" + }, + { + "date": "2020-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2020-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2020-07-31", + "name": "Qurban Bayramı" + }, + { + "date": "2020-08-01", + "name": "Qurban Bayramı" + }, + { + "date": "2020-08-03", + "name": "Qurban Bayramı (əvəz gün)" + }, + { + "date": "2020-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2020-10-19", + "name": "Dövlət Müstəqilliyi günü (əvəz gün)" + }, + { + "date": "2020-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2020-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2020-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2020-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2021-01-01", + "name": "Yeni il" + }, + { + "date": "2021-01-02", + "name": "Yeni il" + }, + { + "date": "2021-01-04", + "name": "Yeni il (əvəz gün)" + }, + { + "date": "2021-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2021-03-20", + "name": "Novruz" + }, + { + "date": "2021-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2021-05-10", + "name": "Faşizm üzərində qələbə günü (əvəz gün)" + }, + { + "date": "2021-05-13", + "name": "Ramazan Bayramı" + }, + { + "date": "2021-05-14", + "name": "Ramazan Bayramı" + }, + { + "date": "2021-05-28", + "name": "Respublika günü" + }, + { + "date": "2021-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2021-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2021-06-28", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü (əvəz gün)" + }, + { + "date": "2021-07-20", + "name": "Qurban Bayramı" + }, + { + "date": "2021-07-21", + "name": "Qurban Bayramı" + }, + { + "date": "2021-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2021-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2021-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2021-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2021-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + } + ] + }, + "BA": { + "name": "Bosna i Hercegovina", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2018-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2018-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2018-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2018-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2018-04-01", + "name": "Vaskrs" + }, + { + "date": "2018-04-02", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2018-04-08", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2018-05-01", + "name": "Radni dan" + }, + { + "date": "2018-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2018-05-31", + "name": "Tijelovo" + }, + { + "date": "2018-06-15", + "name": "Ramazanski bajram" + }, + { + "date": "2018-08-15", + "name": "Velika Gospa" + }, + { + "date": "2018-08-21", + "name": "Kurbanski bajram" + }, + { + "date": "2018-08-28", + "name": "Velika Gospa" + }, + { + "date": "2018-09-11", + "name": "Nova hidžretska godina" + }, + { + "date": "2018-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2018-11-02", + "name": "Dušni dan" + }, + { + "date": "2018-11-20", + "name": "Mevlud" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2019-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2019-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2019-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2019-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2019-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2019-04-21", + "name": "Vaskrs" + }, + { + "date": "2019-04-22", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2019-04-28", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2019-05-01", + "name": "Radni dan" + }, + { + "date": "2019-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2019-06-04", + "name": "Ramazanski bajram" + }, + { + "date": "2019-06-20", + "name": "Tijelovo" + }, + { + "date": "2019-08-11", + "name": "Kurbanski bajram" + }, + { + "date": "2019-08-15", + "name": "Velika Gospa" + }, + { + "date": "2019-08-28", + "name": "Velika Gospa" + }, + { + "date": "2019-08-31", + "name": "Nova hidžretska godina" + }, + { + "date": "2019-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2019-11-02", + "name": "Dušni dan" + }, + { + "date": "2019-11-09", + "name": "Mevlud" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2020-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2020-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2020-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2020-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2020-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2020-04-12", + "name": "Vaskrs" + }, + { + "date": "2020-04-13", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2020-04-19", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2020-05-01", + "name": "Radni dan" + }, + { + "date": "2020-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2020-05-24", + "name": "Ramazanski bajram" + }, + { + "date": "2020-06-11", + "name": "Tijelovo" + }, + { + "date": "2020-07-31", + "name": "Kurbanski bajram" + }, + { + "date": "2020-08-15", + "name": "Velika Gospa" + }, + { + "date": "2020-08-20", + "name": "Nova hidžretska godina" + }, + { + "date": "2020-08-28", + "name": "Velika Gospa" + }, + { + "date": "2020-10-29", + "name": "Mevlud" + }, + { + "date": "2020-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2020-11-02", + "name": "Dušni dan" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2021-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2021-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2021-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2021-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2021-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2021-04-04", + "name": "Vaskrs" + }, + { + "date": "2021-04-05", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2021-05-01", + "name": "Radni dan" + }, + { + "date": "2021-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2021-05-02", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2021-05-03", + "name": "Drugi dan Dana rada (zamjena dan)" + }, + { + "date": "2021-05-13", + "name": "Ramazanski bajram" + }, + { + "date": "2021-06-03", + "name": "Tijelovo" + }, + { + "date": "2021-07-20", + "name": "Kurbanski bajram" + }, + { + "date": "2021-08-09", + "name": "Nova hidžretska godina" + }, + { + "date": "2021-08-15", + "name": "Velika Gospa" + }, + { + "date": "2021-08-28", + "name": "Velika Gospa" + }, + { + "date": "2021-10-18", + "name": "Mevlud" + }, + { + "date": "2021-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2021-11-02", + "name": "Dušni dan" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Svetog Stjepana" + } + ] + }, + "BB": { + "name": "Barbados", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-28", + "name": "National Heroes Day" + }, + { + "date": "2018-05-01", + "name": "May Day" + }, + { + "date": "2018-05-20", + "name": "Pentecost" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-06", + "name": "Kadooment Day" + }, + { + "date": "2018-11-30", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-28", + "name": "National Heroes Day" + }, + { + "date": "2019-05-01", + "name": "May Day" + }, + { + "date": "2019-06-09", + "name": "Pentecost" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-05", + "name": "Kadooment Day" + }, + { + "date": "2019-11-30", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-28", + "name": "National Heroes Day" + }, + { + "date": "2020-05-01", + "name": "May Day" + }, + { + "date": "2020-05-31", + "name": "Pentecost" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-08-03", + "name": "Kadooment Day" + }, + { + "date": "2020-11-30", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-28", + "name": "National Heroes Day" + }, + { + "date": "2021-05-01", + "name": "May Day" + }, + { + "date": "2021-05-23", + "name": "Pentecost" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-01", + "name": "Emancipation Day" + }, + { + "date": "2021-08-02", + "name": "Kadooment Day" + }, + { + "date": "2021-11-30", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" + } + ] + }, + "BE": { + "name": "Belgique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-01", + "name": "Pâques" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-21", + "name": "Fête nationale" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-21", + "name": "Pâques" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-21", + "name": "Fête nationale" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-12", + "name": "Pâques" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-21", + "name": "Fête nationale" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-04", + "name": "Pâques" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-21", + "name": "Fête nationale" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "BG": { + "name": "България", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова Година" + }, + { + "date": "2018-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2018-04-06", + "name": "Разпети петък" + }, + { + "date": "2018-04-08", + "name": "Великден" + }, + { + "date": "2018-04-09", + "name": "Велики понеделник" + }, + { + "date": "2018-05-01", + "name": "Ден на труда" + }, + { + "date": "2018-05-06", + "name": "Гергьовден" + }, + { + "date": "2018-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2018-09-06", + "name": "Ден на съединението" + }, + { + "date": "2018-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2018-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2018-12-25", + "name": "Коледа" + }, + { + "date": "2019-01-01", + "name": "Нова Година" + }, + { + "date": "2019-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2019-04-26", + "name": "Разпети петък" + }, + { + "date": "2019-04-28", + "name": "Великден" + }, + { + "date": "2019-04-29", + "name": "Велики понеделник" + }, + { + "date": "2019-05-01", + "name": "Ден на труда" + }, + { + "date": "2019-05-06", + "name": "Гергьовден" + }, + { + "date": "2019-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2019-09-06", + "name": "Ден на съединението" + }, + { + "date": "2019-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2019-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2019-12-25", + "name": "Коледа" + }, + { + "date": "2020-01-01", + "name": "Нова Година" + }, + { + "date": "2020-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2020-04-17", + "name": "Разпети петък" + }, + { + "date": "2020-04-19", + "name": "Великден" + }, + { + "date": "2020-04-20", + "name": "Велики понеделник" + }, + { + "date": "2020-05-01", + "name": "Ден на труда" + }, + { + "date": "2020-05-06", + "name": "Гергьовден" + }, + { + "date": "2020-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2020-09-06", + "name": "Ден на съединението" + }, + { + "date": "2020-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2020-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2020-12-25", + "name": "Коледа" + }, + { + "date": "2021-01-01", + "name": "Нова Година" + }, + { + "date": "2021-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2021-04-30", + "name": "Разпети петък" + }, + { + "date": "2021-05-01", + "name": "Ден на труда" + }, + { + "date": "2021-05-02", + "name": "Великден" + }, + { + "date": "2021-05-03", + "name": "Велики понеделник" + }, + { + "date": "2021-05-06", + "name": "Гергьовден" + }, + { + "date": "2021-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2021-09-06", + "name": "Ден на съединението" + }, + { + "date": "2021-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2021-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2021-12-25", + "name": "Коледа" + } + ] + }, + "BI": { + "name": "République du Burundi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2018-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2018-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2019-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2019-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2020-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2020-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2021-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2021-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "BL": { + "name": "St. Barthélemy", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "BO": { + "name": "Bolivia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-10", + "name": "La Asunción" + }, + { + "date": "2018-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2018-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2018-08-06", + "name": "Día de la Patria" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-30", + "name": "La Asunción" + }, + { + "date": "2019-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2019-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2019-08-06", + "name": "Día de la Patria" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-21", + "name": "La Asunción" + }, + { + "date": "2020-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2020-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2020-08-06", + "name": "Día de la Patria" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-13", + "name": "La Asunción" + }, + { + "date": "2021-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2021-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2021-08-06", + "name": "Día de la Patria" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "BQ": { + "name": "Caribisch Nederland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-03-30", + "name": "Goede Vrijdag" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2018-05-10", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2018-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-04-19", + "name": "Goede Vrijdag" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2019-05-30", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2019-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-04-10", + "name": "Goede Vrijdag" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2020-05-21", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2020-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-04-02", + "name": "Goede Vrijdag" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2021-05-13", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2021-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + } + ] + }, + "BR": { + "name": "Brasil", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2018-09-07", + "name": "Dia da Independência" + }, + { + "date": "2018-10-07", + "name": "Dia de Eleição" + }, + { + "date": "2018-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2018-10-28", + "name": "Dia de Eleição" + }, + { + "date": "2018-11-02", + "name": "Dia de Finados" + }, + { + "date": "2018-11-15", + "name": "Proclamação da República" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2019-09-07", + "name": "Dia da Independência" + }, + { + "date": "2019-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2019-11-02", + "name": "Dia de Finados" + }, + { + "date": "2019-11-15", + "name": "Proclamação da República" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2020-09-07", + "name": "Dia da Independência" + }, + { + "date": "2020-10-04", + "name": "Dia de Eleição" + }, + { + "date": "2020-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2020-10-25", + "name": "Dia de Eleição" + }, + { + "date": "2020-11-02", + "name": "Dia de Finados" + }, + { + "date": "2020-11-15", + "name": "Proclamação da República" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2021-09-07", + "name": "Dia da Independência" + }, + { + "date": "2021-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2021-11-02", + "name": "Dia de Finados" + }, + { + "date": "2021-11-15", + "name": "Proclamação da República" + }, + { + "date": "2021-12-25", + "name": "Natal" + } + ] + }, + "BS": { + "name": "Bahamas", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-06-01", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2018-07-10", + "name": "Independence Day" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-06-07", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-07-10", + "name": "Independence Day" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2019-10-14", + "name": "National Heroes' Day (substitute day)" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-05", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2020-07-10", + "name": "Independence Day" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2021-01-11", + "name": "Majority Rule Day (substitute day)" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-06-04", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2021-07-10", + "name": "Independence Day" + }, + { + "date": "2021-07-12", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "BW": { + "name": "Botswana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-10", + "name": "Ascension Day" + }, + { + "date": "2018-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2018-07-02", + "name": "Public Holiday" + }, + { + "date": "2018-07-16", + "name": "President’s Day" + }, + { + "date": "2018-07-17", + "name": "President’s Day Holiday" + }, + { + "date": "2018-09-30", + "name": "Botswana Day" + }, + { + "date": "2018-10-01", + "name": "Public Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Family Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-30", + "name": "Ascension Day" + }, + { + "date": "2019-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2019-07-15", + "name": "President’s Day" + }, + { + "date": "2019-07-16", + "name": "President’s Day Holiday" + }, + { + "date": "2019-09-30", + "name": "Botswana Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Family Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-21", + "name": "Ascension Day" + }, + { + "date": "2020-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2020-07-20", + "name": "President’s Day" + }, + { + "date": "2020-07-21", + "name": "President’s Day Holiday" + }, + { + "date": "2020-09-30", + "name": "Botswana Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Family Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Ascension Day" + }, + { + "date": "2021-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2021-07-19", + "name": "President’s Day" + }, + { + "date": "2021-07-20", + "name": "President’s Day Holiday" + }, + { + "date": "2021-09-30", + "name": "Botswana Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Family Day" + } + ] + }, + "BY": { + "name": "Рэспубліка Беларусь", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новы год" + }, + { + "date": "2018-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2018-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2018-04-17", + "name": "Радунiца" + }, + { + "date": "2018-05-01", + "name": "Дзень працы" + }, + { + "date": "2018-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2018-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2018-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2018-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2019-01-01", + "name": "Новы год" + }, + { + "date": "2019-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2019-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2019-05-01", + "name": "Дзень працы" + }, + { + "date": "2019-05-07", + "name": "Радунiца" + }, + { + "date": "2019-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2019-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2019-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2019-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2020-01-01", + "name": "Новы год" + }, + { + "date": "2020-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2020-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2020-04-28", + "name": "Радунiца" + }, + { + "date": "2020-05-01", + "name": "Дзень працы" + }, + { + "date": "2020-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2020-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2020-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2020-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2021-01-01", + "name": "Новы год" + }, + { + "date": "2021-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2021-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2021-05-01", + "name": "Дзень працы" + }, + { + "date": "2021-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2021-05-11", + "name": "Радунiца" + }, + { + "date": "2021-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2021-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2021-12-25", + "name": "Каляды каталiцкiя" + } + ] + }, + "BZ": { + "name": "Belize", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-12", + "name": "Baron Bliss Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2018-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2018-09-21", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "Day of the Americas" + }, + { + "date": "2018-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-11", + "name": "Baron Bliss Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-27", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2019-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2019-09-21", + "name": "Independence Day" + }, + { + "date": "2019-10-14", + "name": "Day of the Americas" + }, + { + "date": "2019-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Baron Bliss Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-25", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2020-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2020-09-21", + "name": "Independence Day" + }, + { + "date": "2020-10-12", + "name": "Day of the Americas" + }, + { + "date": "2020-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-08", + "name": "Baron Bliss Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2021-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2021-09-21", + "name": "Independence Day" + }, + { + "date": "2021-10-11", + "name": "Day of the Americas" + }, + { + "date": "2021-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "CA": { + "name": "Canada", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-05-21", + "name": "Victoria Day" + }, + { + "date": "2018-07-01", + "name": "Canada Day" + }, + { + "date": "2018-08-06", + "name": "Civic Holiday" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Thanksgiving" + }, + { + "date": "2018-11-11", + "name": "Remembrance Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-05-20", + "name": "Victoria Day" + }, + { + "date": "2019-07-01", + "name": "Canada Day" + }, + { + "date": "2019-08-05", + "name": "Civic Holiday" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Thanksgiving" + }, + { + "date": "2019-11-11", + "name": "Remembrance Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-05-18", + "name": "Victoria Day" + }, + { + "date": "2020-07-01", + "name": "Canada Day" + }, + { + "date": "2020-08-03", + "name": "Civic Holiday" + }, + { + "date": "2020-08-31", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Thanksgiving" + }, + { + "date": "2020-11-11", + "name": "Remembrance Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-05-24", + "name": "Victoria Day" + }, + { + "date": "2021-07-01", + "name": "Canada Day" + }, + { + "date": "2021-08-02", + "name": "Civic Holiday" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Thanksgiving" + }, + { + "date": "2021-11-11", + "name": "Remembrance Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "CC": { + "name": "Cocos (Keeling) Islands", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Australia Day" + }, + { + "date": "2018-02-16", + "name": "Chinese New Year" + }, + { + "date": "2018-02-17", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2018-02-19", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2018-03-20", + "name": "Labour Day" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-06", + "name": "Self Determination Day" + }, + { + "date": "2018-04-25", + "name": "Anzac Day" + }, + { + "date": "2018-06-15", + "name": "Hari Raya Puasa" + }, + { + "date": "2018-08-21", + "name": "Hari Raya Haji" + }, + { + "date": "2018-09-11", + "name": "Islamic New Year" + }, + { + "date": "2018-11-20", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Australia Day" + }, + { + "date": "2019-02-05", + "name": "Chinese New Year" + }, + { + "date": "2019-02-06", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2019-03-20", + "name": "Labour Day" + }, + { + "date": "2019-04-06", + "name": "Self Determination Day" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "Anzac Day" + }, + { + "date": "2019-06-04", + "name": "Hari Raya Puasa" + }, + { + "date": "2019-08-11", + "name": "Hari Raya Haji" + }, + { + "date": "2019-08-12", + "name": "Hari Raya Haji (substitute day)" + }, + { + "date": "2019-08-31", + "name": "Islamic New Year" + }, + { + "date": "2019-11-09", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-25", + "name": "Chinese New Year" + }, + { + "date": "2020-01-26", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2020-01-26", + "name": "Australia Day" + }, + { + "date": "2020-01-27", + "name": "Chinese New Year (substitute day)" + }, + { + "date": "2020-01-28", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2020-03-20", + "name": "Labour Day" + }, + { + "date": "2020-04-06", + "name": "Self Determination Day" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-25", + "name": "Anzac Day" + }, + { + "date": "2020-05-24", + "name": "Hari Raya Puasa" + }, + { + "date": "2020-05-25", + "name": "Hari Raya Puasa (substitute day)" + }, + { + "date": "2020-07-31", + "name": "Hari Raya Haji" + }, + { + "date": "2020-08-20", + "name": "Islamic New Year" + }, + { + "date": "2020-10-29", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Australia Day" + }, + { + "date": "2021-02-12", + "name": "Chinese New Year" + }, + { + "date": "2021-02-13", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2021-02-15", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2021-03-20", + "name": "Labour Day" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-06", + "name": "Self Determination Day" + }, + { + "date": "2021-04-25", + "name": "Anzac Day" + }, + { + "date": "2021-05-13", + "name": "Hari Raya Puasa" + }, + { + "date": "2021-07-20", + "name": "Hari Raya Haji" + }, + { + "date": "2021-08-09", + "name": "Islamic New Year" + }, + { + "date": "2021-10-18", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "CD": { + "name": "République démocratique du Congo", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2018-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2018-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2018-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2018-08-01", + "name": "Fête des parents" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2019-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2019-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2019-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2019-07-01", + "name": "Anniversaire de Indépendance (jour substitut)" + }, + { + "date": "2019-08-01", + "name": "Fête des parents" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2020-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2020-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2020-05-18", + "name": "Journée de la Révolution et des Forces Armées (jour substitut)" + }, + { + "date": "2020-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2020-08-01", + "name": "Fête des parents" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2021-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2021-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2021-01-18", + "name": "Journée du Héro National Patrice Emery Lumumba (jour substitut)" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2021-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2021-08-01", + "name": "Fête des parents" + }, + { + "date": "2021-08-02", + "name": "Fête des parents (jour substitut)" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CF": { + "name": "République centrafricaine", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-30", + "name": "Journée de prière" + }, + { + "date": "2018-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-01", + "name": "Jour de la République" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-30", + "name": "Journée de prière" + }, + { + "date": "2019-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-01", + "name": "Jour de la République" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-30", + "name": "Journée de prière" + }, + { + "date": "2020-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-01", + "name": "Jour de la République" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-30", + "name": "Journée de prière" + }, + { + "date": "2021-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-01", + "name": "Jour de la République" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CG": { + "name": "République du Congo", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CH": { + "name": "Schweiz", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-01", + "name": "Ostersonntag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-10", + "name": "Auffahrt" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-08-01", + "name": "Bundesfeier" + }, + { + "date": "2018-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2018-12-26", + "name": "Stephanstag" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-21", + "name": "Ostersonntag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-30", + "name": "Auffahrt" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-08-01", + "name": "Bundesfeier" + }, + { + "date": "2019-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2019-12-26", + "name": "Stephanstag" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-12", + "name": "Ostersonntag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-21", + "name": "Auffahrt" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-08-01", + "name": "Bundesfeier" + }, + { + "date": "2020-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2020-12-26", + "name": "Stephanstag" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-04", + "name": "Ostersonntag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-13", + "name": "Auffahrt" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-08-01", + "name": "Bundesfeier" + }, + { + "date": "2021-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2021-12-26", + "name": "Stephanstag" + } + ] + }, + "CL": { + "name": "Chile", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2018-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2018-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2018-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-11-02", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2019-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2019-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2019-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2019-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2020-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2020-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2020-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2021-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2021-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2021-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2021-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "CM": { + "name": "Cameroun", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CN": { + "name": "中华人民共和国", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "元旦" + }, + { + "date": "2018-02-16", + "name": "春节" + }, + { + "date": "2018-03-08", + "name": "国际妇女节" + }, + { + "date": "2018-04-05", + "name": "清明节 清明節" + }, + { + "date": "2018-05-01", + "name": "劳动节" + }, + { + "date": "2018-05-04", + "name": "青年节" + }, + { + "date": "2018-06-01", + "name": "六一儿童节" + }, + { + "date": "2018-06-18", + "name": "端午节" + }, + { + "date": "2018-08-01", + "name": "建军节" + }, + { + "date": "2018-09-24", + "name": "中秋节" + }, + { + "date": "2018-10-01", + "name": "国庆节" + }, + { + "date": "2019-01-01", + "name": "元旦" + }, + { + "date": "2019-02-05", + "name": "春节" + }, + { + "date": "2019-03-08", + "name": "国际妇女节" + }, + { + "date": "2019-04-05", + "name": "清明节 清明節" + }, + { + "date": "2019-05-01", + "name": "劳动节" + }, + { + "date": "2019-05-04", + "name": "青年节" + }, + { + "date": "2019-06-01", + "name": "六一儿童节" + }, + { + "date": "2019-06-07", + "name": "端午节" + }, + { + "date": "2019-08-01", + "name": "建军节" + }, + { + "date": "2019-09-13", + "name": "中秋节" + }, + { + "date": "2019-10-01", + "name": "国庆节" + }, + { + "date": "2020-01-01", + "name": "元旦" + }, + { + "date": "2020-01-25", + "name": "春节" + }, + { + "date": "2020-03-08", + "name": "国际妇女节" + }, + { + "date": "2020-04-04", + "name": "清明节 清明節" + }, + { + "date": "2020-05-01", + "name": "劳动节" + }, + { + "date": "2020-05-04", + "name": "青年节" + }, + { + "date": "2020-06-01", + "name": "六一儿童节" + }, + { + "date": "2020-06-25", + "name": "端午节" + }, + { + "date": "2020-08-01", + "name": "建军节" + }, + { + "date": "2020-10-01", + "name": "国庆节" + }, + { + "date": "2020-10-01", + "name": "中秋节" + }, + { + "date": "2021-01-01", + "name": "元旦" + }, + { + "date": "2021-02-12", + "name": "春节" + }, + { + "date": "2021-03-08", + "name": "国际妇女节" + }, + { + "date": "2021-04-04", + "name": "清明节 清明節" + }, + { + "date": "2021-05-01", + "name": "劳动节" + }, + { + "date": "2021-05-04", + "name": "青年节" + }, + { + "date": "2021-06-01", + "name": "六一儿童节" + }, + { + "date": "2021-06-14", + "name": "端午节" + }, + { + "date": "2021-08-01", + "name": "建军节" + }, + { + "date": "2021-09-21", + "name": "中秋节" + }, + { + "date": "2021-10-01", + "name": "国庆节" + } + ] + }, + "CO": { + "name": "Colombia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-08", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-03-19", + "name": "San José" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-14", + "name": "La Asunción" + }, + { + "date": "2018-06-04", + "name": "Corpus Christi" + }, + { + "date": "2018-06-11", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2018-07-02", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2018-08-20", + "name": "Asunción" + }, + { + "date": "2018-10-15", + "name": "Día de la Raza" + }, + { + "date": "2018-11-05", + "name": "Todos los Santos" + }, + { + "date": "2018-11-12", + "name": "Independencia de Cartagena" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-07", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-03-25", + "name": "San José" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-03", + "name": "La Asunción" + }, + { + "date": "2019-06-24", + "name": "Corpus Christi" + }, + { + "date": "2019-07-01", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2019-07-01", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2019-08-19", + "name": "Asunción" + }, + { + "date": "2019-10-14", + "name": "Día de la Raza" + }, + { + "date": "2019-11-04", + "name": "Todos los Santos" + }, + { + "date": "2019-11-11", + "name": "Independencia de Cartagena" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-03-23", + "name": "San José" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-25", + "name": "La Asunción" + }, + { + "date": "2020-06-15", + "name": "Corpus Christi" + }, + { + "date": "2020-06-22", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2020-08-17", + "name": "Asunción" + }, + { + "date": "2020-10-12", + "name": "Día de la Raza" + }, + { + "date": "2020-11-02", + "name": "Todos los Santos" + }, + { + "date": "2020-11-16", + "name": "Independencia de Cartagena" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-11", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-03-22", + "name": "San José" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-17", + "name": "La Asunción" + }, + { + "date": "2021-06-07", + "name": "Corpus Christi" + }, + { + "date": "2021-06-14", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2021-07-05", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2021-08-16", + "name": "Asunción" + }, + { + "date": "2021-10-18", + "name": "Día de la Raza" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-11-15", + "name": "Independencia de Cartagena" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CR": { + "name": "Costa Rica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2018-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2018-08-15", + "name": "Día de la Madre" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-12", + "name": "Día de la Raza" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2019-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2019-08-15", + "name": "Día de la Madre" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-12", + "name": "Día de la Raza" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2020-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2020-08-15", + "name": "Día de la Madre" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-09", + "name": "Día de la Raza" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2021-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2021-08-15", + "name": "Día de la Madre" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-12", + "name": "Día de la Raza" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CU": { + "name": "Cuba", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2018-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2018-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2018-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2018-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2019-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2019-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2019-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2019-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2020-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2020-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2020-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2020-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2021-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2021-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2021-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2021-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CW": { + "name": "Curaçao", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-02-12", + "name": "Carnavalmaandag" + }, + { + "date": "2018-03-30", + "name": "Goede Vrijdag" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2018-05-10", + "name": "Hemelvaartsdag" + }, + { + "date": "2018-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2018-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2018-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-03-04", + "name": "Carnavalmaandag" + }, + { + "date": "2019-04-19", + "name": "Goede Vrijdag" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2019-05-30", + "name": "Hemelvaartsdag" + }, + { + "date": "2019-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2019-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-02-24", + "name": "Carnavalmaandag" + }, + { + "date": "2020-04-10", + "name": "Goede Vrijdag" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2020-05-21", + "name": "Hemelvaartsdag" + }, + { + "date": "2020-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2020-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-02-15", + "name": "Carnavalmaandag" + }, + { + "date": "2021-04-02", + "name": "Goede Vrijdag" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2021-05-13", + "name": "Hemelvaartsdag" + }, + { + "date": "2021-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2021-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-12-31", + "name": "Oudejaarsavond" + } + ] + }, + "CY": { + "name": "Κύπρος", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2018-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2018-02-20", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2018-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2018-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2018-04-06", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2018-04-08", + "name": "Πάσχα" + }, + { + "date": "2018-04-09", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2018-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2018-05-27", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2018-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2018-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2018-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2018-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2018-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2019-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2019-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2019-03-12", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2019-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2019-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2019-04-26", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2019-04-28", + "name": "Πάσχα" + }, + { + "date": "2019-04-29", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2019-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2019-06-16", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2019-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2019-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2019-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2019-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2019-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2020-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2020-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2020-03-03", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2020-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2020-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2020-04-17", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2020-04-19", + "name": "Πάσχα" + }, + { + "date": "2020-04-20", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2020-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2020-06-07", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2020-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2020-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2020-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2020-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2020-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2021-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2021-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2021-03-16", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2021-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2021-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2021-04-30", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2021-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2021-05-02", + "name": "Πάσχα" + }, + { + "date": "2021-05-03", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2021-06-20", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2021-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2021-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2021-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2021-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2021-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + } + ] + }, + "CZ": { + "name": "Česká republika", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2018-03-30", + "name": "Velikonoční pátek" + }, + { + "date": "2018-04-01", + "name": "Neděle velikonoční" + }, + { + "date": "2018-04-02", + "name": "Velikonoční pondělí" + }, + { + "date": "2018-05-01", + "name": "Svátek práce" + }, + { + "date": "2018-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2018-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2018-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2018-09-28", + "name": "Den české státnosti" + }, + { + "date": "2018-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2018-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2018-12-24", + "name": "Štědrý den" + }, + { + "date": "2018-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2018-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2019-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2019-04-19", + "name": "Velikonoční pátek" + }, + { + "date": "2019-04-21", + "name": "Neděle velikonoční" + }, + { + "date": "2019-04-22", + "name": "Velikonoční pondělí" + }, + { + "date": "2019-05-01", + "name": "Svátek práce" + }, + { + "date": "2019-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2019-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2019-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2019-09-28", + "name": "Den české státnosti" + }, + { + "date": "2019-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2019-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2019-12-24", + "name": "Štědrý den" + }, + { + "date": "2019-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2019-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2020-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2020-04-10", + "name": "Velikonoční pátek" + }, + { + "date": "2020-04-12", + "name": "Neděle velikonoční" + }, + { + "date": "2020-04-13", + "name": "Velikonoční pondělí" + }, + { + "date": "2020-05-01", + "name": "Svátek práce" + }, + { + "date": "2020-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2020-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2020-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2020-09-28", + "name": "Den české státnosti" + }, + { + "date": "2020-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2020-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2020-12-24", + "name": "Štědrý den" + }, + { + "date": "2020-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2020-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2021-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2021-04-02", + "name": "Velikonoční pátek" + }, + { + "date": "2021-04-04", + "name": "Neděle velikonoční" + }, + { + "date": "2021-04-05", + "name": "Velikonoční pondělí" + }, + { + "date": "2021-05-01", + "name": "Svátek práce" + }, + { + "date": "2021-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2021-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2021-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2021-09-28", + "name": "Den české státnosti" + }, + { + "date": "2021-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2021-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2021-12-24", + "name": "Štědrý den" + }, + { + "date": "2021-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2021-12-26", + "name": "2. svátek vánoční" + } + ] + }, + "DE": { + "name": "Deutschland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-02-13", + "name": "Faschingsdienstag" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Maifeiertag" + }, + { + "date": "2018-05-10", + "name": "Christi Himmelfahrt" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2018-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-03-05", + "name": "Faschingsdienstag" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Maifeiertag" + }, + { + "date": "2019-05-30", + "name": "Christi Himmelfahrt" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2019-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-02-25", + "name": "Faschingsdienstag" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Maifeiertag" + }, + { + "date": "2020-05-21", + "name": "Christi Himmelfahrt" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2020-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-02-16", + "name": "Faschingsdienstag" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Maifeiertag" + }, + { + "date": "2021-05-13", + "name": "Christi Himmelfahrt" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2021-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "DK": { + "name": "Danmark", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nytår" + }, + { + "date": "2018-03-29", + "name": "Skærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Påskesøndag" + }, + { + "date": "2018-04-02", + "name": "Anden påskedag" + }, + { + "date": "2018-04-27", + "name": "Store Bededag" + }, + { + "date": "2018-05-10", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2018-05-20", + "name": "Pinsedag" + }, + { + "date": "2018-05-21", + "name": "2. Pinsedag" + }, + { + "date": "2018-12-25", + "name": "1. Juledag" + }, + { + "date": "2018-12-26", + "name": "2. Juledag" + }, + { + "date": "2019-01-01", + "name": "Nytår" + }, + { + "date": "2019-04-18", + "name": "Skærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Påskesøndag" + }, + { + "date": "2019-04-22", + "name": "Anden påskedag" + }, + { + "date": "2019-05-17", + "name": "Store Bededag" + }, + { + "date": "2019-05-30", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Pinsedag" + }, + { + "date": "2019-06-10", + "name": "2. Pinsedag" + }, + { + "date": "2019-12-25", + "name": "1. Juledag" + }, + { + "date": "2019-12-26", + "name": "2. Juledag" + }, + { + "date": "2020-01-01", + "name": "Nytår" + }, + { + "date": "2020-04-09", + "name": "Skærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Påskesøndag" + }, + { + "date": "2020-04-13", + "name": "Anden påskedag" + }, + { + "date": "2020-05-08", + "name": "Store Bededag" + }, + { + "date": "2020-05-21", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Pinsedag" + }, + { + "date": "2020-06-01", + "name": "2. Pinsedag" + }, + { + "date": "2020-12-25", + "name": "1. Juledag" + }, + { + "date": "2020-12-26", + "name": "2. Juledag" + }, + { + "date": "2021-01-01", + "name": "Nytår" + }, + { + "date": "2021-04-01", + "name": "Skærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Påskesøndag" + }, + { + "date": "2021-04-05", + "name": "Anden påskedag" + }, + { + "date": "2021-04-30", + "name": "Store Bededag" + }, + { + "date": "2021-05-13", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2021-05-23", + "name": "Pinsedag" + }, + { + "date": "2021-05-24", + "name": "2. Pinsedag" + }, + { + "date": "2021-12-25", + "name": "1. Juledag" + }, + { + "date": "2021-12-26", + "name": "2. Juledag" + } + ] + }, + "DM": { + "name": "Dominica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-12", + "name": "Carnival Monday" + }, + { + "date": "2018-02-13", + "name": "Carnival Tuesday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-11-03", + "name": "Independence Day" + }, + { + "date": "2018-11-05", + "name": "National Day of Community Service" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-04", + "name": "Carnival Monday" + }, + { + "date": "2019-03-05", + "name": "Carnival Tuesday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-11-04", + "name": "Independence Day" + }, + { + "date": "2019-11-05", + "name": "National Day of Community Service" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-24", + "name": "Carnival Monday" + }, + { + "date": "2020-02-25", + "name": "Carnival Tuesday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-11-03", + "name": "Independence Day" + }, + { + "date": "2020-11-04", + "name": "National Day of Community Service" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-15", + "name": "Carnival Monday" + }, + { + "date": "2021-02-16", + "name": "Carnival Tuesday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-11-03", + "name": "Independence Day" + }, + { + "date": "2021-11-04", + "name": "National Day of Community Service" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "DO": { + "name": "República Dominicana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2018-01-29", + "name": "Día de Duarte" + }, + { + "date": "2018-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-30", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2018-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2018-11-12", + "name": "Día de la Constitución" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2019-01-26", + "name": "Día de Duarte" + }, + { + "date": "2019-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-29", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2019-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2019-11-11", + "name": "Día de la Constitución" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2020-01-26", + "name": "Día de Duarte" + }, + { + "date": "2020-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-04", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2020-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2020-11-09", + "name": "Día de la Constitución" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-04", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2021-01-25", + "name": "Día de Duarte" + }, + { + "date": "2021-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2021-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2021-11-08", + "name": "Día de la Constitución" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "EC": { + "name": "Ecuador", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2018-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2018-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2019-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2019-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2020-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2020-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2021-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2021-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "EE": { + "name": "Eesti", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "uusaasta" + }, + { + "date": "2018-03-30", + "name": "suur reede" + }, + { + "date": "2018-04-01", + "name": "lihavõtted" + }, + { + "date": "2018-05-01", + "name": "kevadpüha" + }, + { + "date": "2018-05-20", + "name": "nelipühade 1. püha" + }, + { + "date": "2018-06-23", + "name": "võidupüha" + }, + { + "date": "2018-06-24", + "name": "jaanipäev" + }, + { + "date": "2018-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2018-12-24", + "name": "jõululaupäev" + }, + { + "date": "2018-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2018-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2019-01-01", + "name": "uusaasta" + }, + { + "date": "2019-04-19", + "name": "suur reede" + }, + { + "date": "2019-04-21", + "name": "lihavõtted" + }, + { + "date": "2019-05-01", + "name": "kevadpüha" + }, + { + "date": "2019-06-09", + "name": "nelipühade 1. püha" + }, + { + "date": "2019-06-23", + "name": "võidupüha" + }, + { + "date": "2019-06-24", + "name": "jaanipäev" + }, + { + "date": "2019-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2019-12-24", + "name": "jõululaupäev" + }, + { + "date": "2019-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2019-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2020-01-01", + "name": "uusaasta" + }, + { + "date": "2020-04-10", + "name": "suur reede" + }, + { + "date": "2020-04-12", + "name": "lihavõtted" + }, + { + "date": "2020-05-01", + "name": "kevadpüha" + }, + { + "date": "2020-05-31", + "name": "nelipühade 1. püha" + }, + { + "date": "2020-06-23", + "name": "võidupüha" + }, + { + "date": "2020-06-24", + "name": "jaanipäev" + }, + { + "date": "2020-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2020-12-24", + "name": "jõululaupäev" + }, + { + "date": "2020-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2020-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2021-01-01", + "name": "uusaasta" + }, + { + "date": "2021-04-02", + "name": "suur reede" + }, + { + "date": "2021-04-04", + "name": "lihavõtted" + }, + { + "date": "2021-05-01", + "name": "kevadpüha" + }, + { + "date": "2021-05-23", + "name": "nelipühade 1. püha" + }, + { + "date": "2021-06-23", + "name": "võidupüha" + }, + { + "date": "2021-06-24", + "name": "jaanipäev" + }, + { + "date": "2021-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2021-12-24", + "name": "jõululaupäev" + }, + { + "date": "2021-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2021-12-26", + "name": "teine jõulupüha" + } + ] + }, + "ES": { + "name": "España", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-03-19", + "name": "San José" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-01-07", + "name": "Día de los Reyes Magos (día sustituto)" + }, + { + "date": "2019-03-19", + "name": "San José" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-09", + "name": "La inmaculada concepción (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-03-19", + "name": "San José" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-11-02", + "name": "Todos los Santos (día sustituto)" + }, + { + "date": "2020-12-07", + "name": "Día de la Constitución Española" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-03-19", + "name": "San José" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-08-16", + "name": "Asunción (día sustituto)" + }, + { + "date": "2021-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "ET": { + "name": "ኢትዮጵያ", + "bank_holidays": [ + { + "date": "2018-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2018-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2018-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2018-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2018-04-06", + "name": "ስቅለት" + }, + { + "date": "2018-04-08", + "name": "ፋሲካ" + }, + { + "date": "2018-05-16", + "name": "ረመዳን" + }, + { + "date": "2018-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2018-06-15", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2018-08-21", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2018-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2018-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2018-11-20", + "name": "መውሊድ" + }, + { + "date": "2019-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2019-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2019-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2019-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2019-04-26", + "name": "ስቅለት" + }, + { + "date": "2019-04-28", + "name": "ፋሲካ" + }, + { + "date": "2019-05-06", + "name": "ረመዳን" + }, + { + "date": "2019-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2019-06-04", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2019-08-11", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2019-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2019-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2019-11-09", + "name": "መውሊድ" + }, + { + "date": "2020-01-07", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2020-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2020-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2020-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2020-04-17", + "name": "ስቅለት" + }, + { + "date": "2020-04-19", + "name": "ፋሲካ" + }, + { + "date": "2020-04-24", + "name": "ረመዳን" + }, + { + "date": "2020-05-24", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2020-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2020-07-31", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2020-09-12", + "name": "እንቁጣጣሽ" + }, + { + "date": "2020-09-28", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2020-10-29", + "name": "መውሊድ" + }, + { + "date": "2021-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2021-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2021-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2021-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2021-04-13", + "name": "ረመዳን" + }, + { + "date": "2021-04-30", + "name": "ስቅለት" + }, + { + "date": "2021-05-02", + "name": "ፋሲካ" + }, + { + "date": "2021-05-13", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2021-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2021-07-20", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2021-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2021-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2021-10-18", + "name": "መውሊድ" + } + ] + }, + "FI": { + "name": "Suomi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2018-01-06", + "name": "Loppiainen" + }, + { + "date": "2018-03-30", + "name": "Pitkäperjantai" + }, + { + "date": "2018-04-01", + "name": "Pääsiäispäivä" + }, + { + "date": "2018-04-02", + "name": "2. pääsiäispäivä" + }, + { + "date": "2018-05-01", + "name": "Vappu" + }, + { + "date": "2018-05-10", + "name": "Helatorstai" + }, + { + "date": "2018-05-20", + "name": "Helluntaipäivä" + }, + { + "date": "2018-06-22", + "name": "Juhannusaatto" + }, + { + "date": "2018-06-23", + "name": "Juhannuspäivä" + }, + { + "date": "2018-11-03", + "name": "Pyhäinpäivä" + }, + { + "date": "2018-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2018-12-24", + "name": "Jouluaatto" + }, + { + "date": "2018-12-25", + "name": "Joulupäivä" + }, + { + "date": "2018-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2018-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2019-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2019-01-06", + "name": "Loppiainen" + }, + { + "date": "2019-04-19", + "name": "Pitkäperjantai" + }, + { + "date": "2019-04-21", + "name": "Pääsiäispäivä" + }, + { + "date": "2019-04-22", + "name": "2. pääsiäispäivä" + }, + { + "date": "2019-05-01", + "name": "Vappu" + }, + { + "date": "2019-05-30", + "name": "Helatorstai" + }, + { + "date": "2019-06-09", + "name": "Helluntaipäivä" + }, + { + "date": "2019-06-21", + "name": "Juhannusaatto" + }, + { + "date": "2019-06-22", + "name": "Juhannuspäivä" + }, + { + "date": "2019-11-02", + "name": "Pyhäinpäivä" + }, + { + "date": "2019-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2019-12-24", + "name": "Jouluaatto" + }, + { + "date": "2019-12-25", + "name": "Joulupäivä" + }, + { + "date": "2019-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2019-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2020-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2020-01-06", + "name": "Loppiainen" + }, + { + "date": "2020-04-10", + "name": "Pitkäperjantai" + }, + { + "date": "2020-04-12", + "name": "Pääsiäispäivä" + }, + { + "date": "2020-04-13", + "name": "2. pääsiäispäivä" + }, + { + "date": "2020-05-01", + "name": "Vappu" + }, + { + "date": "2020-05-21", + "name": "Helatorstai" + }, + { + "date": "2020-05-31", + "name": "Helluntaipäivä" + }, + { + "date": "2020-06-19", + "name": "Juhannusaatto" + }, + { + "date": "2020-06-20", + "name": "Juhannuspäivä" + }, + { + "date": "2020-10-31", + "name": "Pyhäinpäivä" + }, + { + "date": "2020-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2020-12-24", + "name": "Jouluaatto" + }, + { + "date": "2020-12-25", + "name": "Joulupäivä" + }, + { + "date": "2020-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2020-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2021-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2021-01-06", + "name": "Loppiainen" + }, + { + "date": "2021-04-02", + "name": "Pitkäperjantai" + }, + { + "date": "2021-04-04", + "name": "Pääsiäispäivä" + }, + { + "date": "2021-04-05", + "name": "2. pääsiäispäivä" + }, + { + "date": "2021-05-01", + "name": "Vappu" + }, + { + "date": "2021-05-13", + "name": "Helatorstai" + }, + { + "date": "2021-05-23", + "name": "Helluntaipäivä" + }, + { + "date": "2021-06-25", + "name": "Juhannusaatto" + }, + { + "date": "2021-06-26", + "name": "Juhannuspäivä" + }, + { + "date": "2021-11-06", + "name": "Pyhäinpäivä" + }, + { + "date": "2021-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2021-12-24", + "name": "Jouluaatto" + }, + { + "date": "2021-12-25", + "name": "Joulupäivä" + }, + { + "date": "2021-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2021-12-31", + "name": "Uudenvuodenaatto" + } + ] + }, + "FO": { + "name": "Føroyar", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2018-03-29", + "name": "Skírhósdagur" + }, + { + "date": "2018-03-30", + "name": "Langafríggjadagur" + }, + { + "date": "2018-04-01", + "name": "Páskadagur" + }, + { + "date": "2018-04-02", + "name": "Annar páskadagur" + }, + { + "date": "2018-04-24", + "name": "Flaggdagur" + }, + { + "date": "2018-04-27", + "name": "Dýri biðidagur" + }, + { + "date": "2018-05-10", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2018-05-20", + "name": "Hvítusunnudagur" + }, + { + "date": "2018-05-21", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2018-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2018-12-24", + "name": "Jólaaftan" + }, + { + "date": "2018-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2018-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2018-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2019-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2019-04-18", + "name": "Skírhósdagur" + }, + { + "date": "2019-04-19", + "name": "Langafríggjadagur" + }, + { + "date": "2019-04-21", + "name": "Páskadagur" + }, + { + "date": "2019-04-22", + "name": "Annar páskadagur" + }, + { + "date": "2019-04-24", + "name": "Flaggdagur" + }, + { + "date": "2019-05-17", + "name": "Dýri biðidagur" + }, + { + "date": "2019-05-30", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2019-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2019-06-09", + "name": "Hvítusunnudagur" + }, + { + "date": "2019-06-10", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2019-12-24", + "name": "Jólaaftan" + }, + { + "date": "2019-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2019-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2019-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2020-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2020-04-09", + "name": "Skírhósdagur" + }, + { + "date": "2020-04-10", + "name": "Langafríggjadagur" + }, + { + "date": "2020-04-12", + "name": "Páskadagur" + }, + { + "date": "2020-04-13", + "name": "Annar páskadagur" + }, + { + "date": "2020-04-24", + "name": "Flaggdagur" + }, + { + "date": "2020-05-08", + "name": "Dýri biðidagur" + }, + { + "date": "2020-05-21", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2020-05-31", + "name": "Hvítusunnudagur" + }, + { + "date": "2020-06-01", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2020-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2020-12-24", + "name": "Jólaaftan" + }, + { + "date": "2020-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2020-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2020-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2021-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2021-04-01", + "name": "Skírhósdagur" + }, + { + "date": "2021-04-02", + "name": "Langafríggjadagur" + }, + { + "date": "2021-04-04", + "name": "Páskadagur" + }, + { + "date": "2021-04-05", + "name": "Annar páskadagur" + }, + { + "date": "2021-04-24", + "name": "Flaggdagur" + }, + { + "date": "2021-04-30", + "name": "Dýri biðidagur" + }, + { + "date": "2021-05-13", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2021-05-23", + "name": "Hvítusunnudagur" + }, + { + "date": "2021-05-24", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2021-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2021-12-24", + "name": "Jólaaftan" + }, + { + "date": "2021-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2021-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2021-12-31", + "name": "Nýggjársaftan" + } + ] + }, + "FR": { + "name": "France", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GA": { + "name": "Gabon", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GB": { + "name": "United Kingdom", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GD": { + "name": "Grenada", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-07", + "name": "Independence Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-08-13", + "name": "Carnival Monday" + }, + { + "date": "2018-08-14", + "name": "Carnival Tuesday" + }, + { + "date": "2018-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-02-07", + "name": "Independence Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-08-12", + "name": "Carnival Monday" + }, + { + "date": "2019-08-13", + "name": "Carnival Tuesday" + }, + { + "date": "2019-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-07", + "name": "Independence Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-08-10", + "name": "Carnival Monday" + }, + { + "date": "2020-08-11", + "name": "Carnival Tuesday" + }, + { + "date": "2020-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-07", + "name": "Independence Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-08-09", + "name": "Carnival Monday" + }, + { + "date": "2021-08-10", + "name": "Carnival Tuesday" + }, + { + "date": "2021-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "GF": { + "name": "Guyane", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GG": { + "name": "Guernsey", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-09", + "name": "Liberation Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-09", + "name": "Liberation Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-09", + "name": "Liberation Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-09", + "name": "Liberation Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GI": { + "name": "Gibraltar", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-12", + "name": "Commonwealth Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2018-05-01", + "name": "May Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-06-18", + "name": "Queen's Birthday" + }, + { + "date": "2018-08-27", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2018-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-11", + "name": "Commonwealth Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2019-05-01", + "name": "May Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-06-17", + "name": "Queen's Birthday" + }, + { + "date": "2019-08-26", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2019-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Commonwealth Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2020-05-01", + "name": "May Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-06-15", + "name": "Queen's Birthday" + }, + { + "date": "2020-08-31", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2020-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-08", + "name": "Commonwealth Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2021-05-03", + "name": "May Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-06-14", + "name": "Queen's Birthday" + }, + { + "date": "2021-08-30", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2021-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GL": { + "name": "Kalaallit Nunaat", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "ukiortaaq" + }, + { + "date": "2018-01-06", + "name": "Åbenbaring" + }, + { + "date": "2018-03-29", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2018-03-30", + "name": "tallimanngornersuaq" + }, + { + "date": "2018-04-01", + "name": "poorskip-ullua" + }, + { + "date": "2018-04-02", + "name": "poorskip-aappaa" + }, + { + "date": "2018-04-27", + "name": "tussiarfissuaq" + }, + { + "date": "2018-05-10", + "name": "qilaliarfik" + }, + { + "date": "2018-05-20", + "name": "piinsip ullua" + }, + { + "date": "2018-05-21", + "name": "piinsip aappaa" + }, + { + "date": "2018-06-21", + "name": "ullortuneq" + }, + { + "date": "2018-12-24", + "name": "juulliaraq" + }, + { + "date": "2018-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2018-12-26", + "name": "juullip aappaa" + }, + { + "date": "2019-01-01", + "name": "ukiortaaq" + }, + { + "date": "2019-01-06", + "name": "Åbenbaring" + }, + { + "date": "2019-04-18", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2019-04-19", + "name": "tallimanngornersuaq" + }, + { + "date": "2019-04-21", + "name": "poorskip-ullua" + }, + { + "date": "2019-04-22", + "name": "poorskip-aappaa" + }, + { + "date": "2019-05-17", + "name": "tussiarfissuaq" + }, + { + "date": "2019-05-30", + "name": "qilaliarfik" + }, + { + "date": "2019-06-09", + "name": "piinsip ullua" + }, + { + "date": "2019-06-10", + "name": "piinsip aappaa" + }, + { + "date": "2019-06-21", + "name": "ullortuneq" + }, + { + "date": "2019-12-24", + "name": "juulliaraq" + }, + { + "date": "2019-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2019-12-26", + "name": "juullip aappaa" + }, + { + "date": "2020-01-01", + "name": "ukiortaaq" + }, + { + "date": "2020-01-06", + "name": "Åbenbaring" + }, + { + "date": "2020-04-09", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2020-04-10", + "name": "tallimanngornersuaq" + }, + { + "date": "2020-04-12", + "name": "poorskip-ullua" + }, + { + "date": "2020-04-13", + "name": "poorskip-aappaa" + }, + { + "date": "2020-05-08", + "name": "tussiarfissuaq" + }, + { + "date": "2020-05-21", + "name": "qilaliarfik" + }, + { + "date": "2020-05-31", + "name": "piinsip ullua" + }, + { + "date": "2020-06-01", + "name": "piinsip aappaa" + }, + { + "date": "2020-06-21", + "name": "ullortuneq" + }, + { + "date": "2020-12-24", + "name": "juulliaraq" + }, + { + "date": "2020-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2020-12-26", + "name": "juullip aappaa" + }, + { + "date": "2021-01-01", + "name": "ukiortaaq" + }, + { + "date": "2021-01-06", + "name": "Åbenbaring" + }, + { + "date": "2021-04-01", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2021-04-02", + "name": "tallimanngornersuaq" + }, + { + "date": "2021-04-04", + "name": "poorskip-ullua" + }, + { + "date": "2021-04-05", + "name": "poorskip-aappaa" + }, + { + "date": "2021-04-30", + "name": "tussiarfissuaq" + }, + { + "date": "2021-05-13", + "name": "qilaliarfik" + }, + { + "date": "2021-05-23", + "name": "piinsip ullua" + }, + { + "date": "2021-05-24", + "name": "piinsip aappaa" + }, + { + "date": "2021-06-21", + "name": "ullortuneq" + }, + { + "date": "2021-12-24", + "name": "juulliaraq" + }, + { + "date": "2021-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2021-12-26", + "name": "juullip aappaa" + } + ] + }, + "GP": { + "name": "Guadeloupe", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GQ": { + "name": "República de Guinea Ecuatorial", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2018-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2018-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2018-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2019-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2019-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-09", + "name": "La inmaculada concepción (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2020-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2020-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2021-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2021-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2021-08-16", + "name": "Día de la Constitución (día sustituto)" + }, + { + "date": "2021-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "GR": { + "name": "Ελλάδα", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2018-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2018-02-19", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2018-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2018-04-06", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2018-04-08", + "name": "Πάσχα" + }, + { + "date": "2018-04-09", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2018-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2018-05-27", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2018-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2018-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2018-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2018-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2019-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2019-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2019-03-11", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2019-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2019-04-26", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2019-04-28", + "name": "Πάσχα" + }, + { + "date": "2019-04-29", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2019-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2019-06-16", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2019-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2019-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2019-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2019-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2020-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2020-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2020-03-02", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2020-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2020-04-17", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2020-04-19", + "name": "Πάσχα" + }, + { + "date": "2020-04-20", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2020-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2020-06-07", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2020-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2020-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2020-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2020-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2021-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2021-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2021-03-15", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2021-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2021-04-30", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2021-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2021-05-02", + "name": "Πάσχα" + }, + { + "date": "2021-05-03", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2021-06-20", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2021-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2021-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2021-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2021-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + } + ] + }, + "GT": { + "name": "Guatemala", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-03-31", + "name": "Sabado Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-30", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-20", + "name": "Sabado Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-30", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Sabado Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-29", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-03", + "name": "Sabado Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-02", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "GU": { + "name": "Guam", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-07-21", + "name": "Liberation Day" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-02", + "name": "All Souls' Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-07-21", + "name": "Liberation Day" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-02", + "name": "All Souls' Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-07-21", + "name": "Liberation Day" + }, + { + "date": "2020-09-07", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-02", + "name": "All Souls' Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-07-21", + "name": "Liberation Day" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-02", + "name": "All Souls' Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + } + ] + }, + "GY": { + "name": "Guyana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-23", + "name": "Republic Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-05", + "name": "Arrival Day" + }, + { + "date": "2018-05-26", + "name": "Independence Day" + }, + { + "date": "2018-07-02", + "name": "CARICOM Day" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-21", + "name": "Eid Ul Adha" + }, + { + "date": "2018-11-20", + "name": "Youman Nabi" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-02-23", + "name": "Republic Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-05", + "name": "Arrival Day" + }, + { + "date": "2019-05-26", + "name": "Independence Day" + }, + { + "date": "2019-07-01", + "name": "CARICOM Day" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-11", + "name": "Eid Ul Adha" + }, + { + "date": "2019-11-09", + "name": "Youman Nabi" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-23", + "name": "Republic Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-05", + "name": "Arrival Day" + }, + { + "date": "2020-05-26", + "name": "Independence Day" + }, + { + "date": "2020-07-06", + "name": "CARICOM Day" + }, + { + "date": "2020-07-31", + "name": "Eid Ul Adha" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-10-29", + "name": "Youman Nabi" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-23", + "name": "Republic Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-05", + "name": "Arrival Day" + }, + { + "date": "2021-05-26", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "CARICOM Day" + }, + { + "date": "2021-07-20", + "name": "Eid Ul Adha" + }, + { + "date": "2021-08-01", + "name": "Emancipation Day" + }, + { + "date": "2021-10-18", + "name": "Youman Nabi" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "HN": { + "name": "Honduras", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-14", + "name": "Día de las Américas" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-03", + "name": "Día del Soldado" + }, + { + "date": "2018-10-12", + "name": "Día de la Raza" + }, + { + "date": "2018-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-14", + "name": "Día de las Américas" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-03", + "name": "Día del Soldado" + }, + { + "date": "2019-10-12", + "name": "Día de la Raza" + }, + { + "date": "2019-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-14", + "name": "Día de las Américas" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-03", + "name": "Día del Soldado" + }, + { + "date": "2020-10-12", + "name": "Día de la Raza" + }, + { + "date": "2020-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-14", + "name": "Día de las Américas" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-03", + "name": "Día del Soldado" + }, + { + "date": "2021-10-12", + "name": "Día de la Raza" + }, + { + "date": "2021-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "HR": { + "name": "Hrvatska", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nova godina" + }, + { + "date": "2018-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2018-02-13", + "name": "Pokladni utorak" + }, + { + "date": "2018-04-02", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2018-05-01", + "name": "Praznik rada" + }, + { + "date": "2018-05-31", + "name": "Tijelovo" + }, + { + "date": "2018-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2018-06-25", + "name": "Dan državnosti" + }, + { + "date": "2018-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2018-08-15", + "name": "Velika Gospa" + }, + { + "date": "2018-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2018-11-01", + "name": "Svi sveti" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2019-01-01", + "name": "Nova godina" + }, + { + "date": "2019-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2019-03-05", + "name": "Pokladni utorak" + }, + { + "date": "2019-04-22", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2019-05-01", + "name": "Praznik rada" + }, + { + "date": "2019-06-20", + "name": "Tijelovo" + }, + { + "date": "2019-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2019-06-25", + "name": "Dan državnosti" + }, + { + "date": "2019-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2019-08-15", + "name": "Velika Gospa" + }, + { + "date": "2019-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2019-11-01", + "name": "Svi sveti" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2020-01-01", + "name": "Nova godina" + }, + { + "date": "2020-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2020-02-25", + "name": "Pokladni utorak" + }, + { + "date": "2020-04-13", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2020-05-01", + "name": "Praznik rada" + }, + { + "date": "2020-06-11", + "name": "Tijelovo" + }, + { + "date": "2020-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2020-06-25", + "name": "Dan državnosti" + }, + { + "date": "2020-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2020-08-15", + "name": "Velika Gospa" + }, + { + "date": "2020-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2020-11-01", + "name": "Svi sveti" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2021-01-01", + "name": "Nova godina" + }, + { + "date": "2021-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2021-02-16", + "name": "Pokladni utorak" + }, + { + "date": "2021-04-05", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2021-05-01", + "name": "Praznik rada" + }, + { + "date": "2021-06-03", + "name": "Tijelovo" + }, + { + "date": "2021-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2021-06-25", + "name": "Dan državnosti" + }, + { + "date": "2021-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2021-08-15", + "name": "Velika Gospa" + }, + { + "date": "2021-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2021-11-01", + "name": "Svi sveti" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Svetog Stjepana" + } + ] + }, + "HT": { + "name": "Haïti", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2018-02-12", + "name": "Lundi Gras" + }, + { + "date": "2018-02-13", + "name": "Mardi Gras" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-01", + "name": "Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2018-05-31", + "name": "la Fête-Dieu" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-02", + "name": "Fête des morts" + }, + { + "date": "2018-11-18", + "name": "Vertières" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2019-03-04", + "name": "Lundi Gras" + }, + { + "date": "2019-03-05", + "name": "Mardi Gras" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-21", + "name": "Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-20", + "name": "la Fête-Dieu" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-02", + "name": "Fête des morts" + }, + { + "date": "2019-11-18", + "name": "Vertières" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2020-02-24", + "name": "Lundi Gras" + }, + { + "date": "2020-02-25", + "name": "Mardi Gras" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-12", + "name": "Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-11", + "name": "la Fête-Dieu" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-02", + "name": "Fête des morts" + }, + { + "date": "2020-11-18", + "name": "Vertières" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2021-02-15", + "name": "Lundi Gras" + }, + { + "date": "2021-02-16", + "name": "Mardi Gras" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-04", + "name": "Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2021-06-03", + "name": "la Fête-Dieu" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-02", + "name": "Fête des morts" + }, + { + "date": "2021-11-18", + "name": "Vertières" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "HU": { + "name": "Magyarország", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Újév" + }, + { + "date": "2018-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2018-04-01", + "name": "Húsvétvasárnap" + }, + { + "date": "2018-04-02", + "name": "Húsvéthétfő" + }, + { + "date": "2018-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2018-05-20", + "name": "Pünkösdvasárnap" + }, + { + "date": "2018-05-21", + "name": "Pünkösdhétfő" + }, + { + "date": "2018-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2018-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2018-11-01", + "name": "Mindenszentek" + }, + { + "date": "2018-12-25", + "name": "Karácsony" + }, + { + "date": "2018-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2019-01-01", + "name": "Újév" + }, + { + "date": "2019-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2019-04-21", + "name": "Húsvétvasárnap" + }, + { + "date": "2019-04-22", + "name": "Húsvéthétfő" + }, + { + "date": "2019-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2019-06-09", + "name": "Pünkösdvasárnap" + }, + { + "date": "2019-06-10", + "name": "Pünkösdhétfő" + }, + { + "date": "2019-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2019-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2019-11-01", + "name": "Mindenszentek" + }, + { + "date": "2019-12-25", + "name": "Karácsony" + }, + { + "date": "2019-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2020-01-01", + "name": "Újév" + }, + { + "date": "2020-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2020-04-12", + "name": "Húsvétvasárnap" + }, + { + "date": "2020-04-13", + "name": "Húsvéthétfő" + }, + { + "date": "2020-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2020-05-31", + "name": "Pünkösdvasárnap" + }, + { + "date": "2020-06-01", + "name": "Pünkösdhétfő" + }, + { + "date": "2020-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2020-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2020-11-01", + "name": "Mindenszentek" + }, + { + "date": "2020-12-25", + "name": "Karácsony" + }, + { + "date": "2020-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2021-01-01", + "name": "Újév" + }, + { + "date": "2021-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2021-04-04", + "name": "Húsvétvasárnap" + }, + { + "date": "2021-04-05", + "name": "Húsvéthétfő" + }, + { + "date": "2021-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2021-05-23", + "name": "Pünkösdvasárnap" + }, + { + "date": "2021-05-24", + "name": "Pünkösdhétfő" + }, + { + "date": "2021-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2021-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2021-11-01", + "name": "Mindenszentek" + }, + { + "date": "2021-12-25", + "name": "Karácsony" + }, + { + "date": "2021-12-26", + "name": "Karácsony másnapja" + } + ] + }, + "IE": { + "name": "Ireland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "May Day" + }, + { + "date": "2018-06-04", + "name": "First Monday in June" + }, + { + "date": "2018-08-06", + "name": "First Monday in August" + }, + { + "date": "2018-10-29", + "name": "October Bank Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "May Day" + }, + { + "date": "2019-06-03", + "name": "First Monday in June" + }, + { + "date": "2019-08-05", + "name": "First Monday in August" + }, + { + "date": "2019-10-28", + "name": "October Bank Holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "May Day" + }, + { + "date": "2020-06-01", + "name": "First Monday in June" + }, + { + "date": "2020-08-03", + "name": "First Monday in August" + }, + { + "date": "2020-10-26", + "name": "October Bank Holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "May Day" + }, + { + "date": "2021-06-07", + "name": "First Monday in June" + }, + { + "date": "2021-08-02", + "name": "First Monday in August" + }, + { + "date": "2021-10-25", + "name": "October Bank Holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "St. Stephen's Day" + } + ] + }, + "IM": { + "name": "Isle of Man", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-06-08", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2018-07-05", + "name": "Tynwald Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-06-14", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2019-07-05", + "name": "Tynwald Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-06-12", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2020-07-05", + "name": "Tynwald Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-06-11", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2021-07-05", + "name": "Tynwald Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "IS": { + "name": "Ísland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nýársdagur" + }, + { + "date": "2018-03-29", + "name": "Skírdagur" + }, + { + "date": "2018-03-30", + "name": "Föstudagurinn langi" + }, + { + "date": "2018-04-01", + "name": "Páskadagur" + }, + { + "date": "2018-04-02", + "name": "Annar í páskum" + }, + { + "date": "2018-04-19", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2018-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2018-05-10", + "name": "Uppstigningardagur" + }, + { + "date": "2018-05-20", + "name": "Hvítasunnudagur" + }, + { + "date": "2018-05-21", + "name": "Annar í hvítasunnu" + }, + { + "date": "2018-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2018-08-06", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2018-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2018-12-25", + "name": "Jóladagur" + }, + { + "date": "2018-12-26", + "name": "Annar í jólum" + }, + { + "date": "2018-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2019-01-01", + "name": "Nýársdagur" + }, + { + "date": "2019-04-18", + "name": "Skírdagur" + }, + { + "date": "2019-04-18", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2019-04-19", + "name": "Föstudagurinn langi" + }, + { + "date": "2019-04-21", + "name": "Páskadagur" + }, + { + "date": "2019-04-22", + "name": "Annar í páskum" + }, + { + "date": "2019-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2019-05-30", + "name": "Uppstigningardagur" + }, + { + "date": "2019-06-09", + "name": "Hvítasunnudagur" + }, + { + "date": "2019-06-10", + "name": "Annar í hvítasunnu" + }, + { + "date": "2019-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2019-08-05", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2019-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2019-12-25", + "name": "Jóladagur" + }, + { + "date": "2019-12-26", + "name": "Annar í jólum" + }, + { + "date": "2019-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2020-01-01", + "name": "Nýársdagur" + }, + { + "date": "2020-04-09", + "name": "Skírdagur" + }, + { + "date": "2020-04-10", + "name": "Föstudagurinn langi" + }, + { + "date": "2020-04-12", + "name": "Páskadagur" + }, + { + "date": "2020-04-13", + "name": "Annar í páskum" + }, + { + "date": "2020-04-23", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2020-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2020-05-21", + "name": "Uppstigningardagur" + }, + { + "date": "2020-05-31", + "name": "Hvítasunnudagur" + }, + { + "date": "2020-06-01", + "name": "Annar í hvítasunnu" + }, + { + "date": "2020-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2020-08-03", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2020-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2020-12-25", + "name": "Jóladagur" + }, + { + "date": "2020-12-26", + "name": "Annar í jólum" + }, + { + "date": "2020-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2021-01-01", + "name": "Nýársdagur" + }, + { + "date": "2021-04-01", + "name": "Skírdagur" + }, + { + "date": "2021-04-02", + "name": "Föstudagurinn langi" + }, + { + "date": "2021-04-04", + "name": "Páskadagur" + }, + { + "date": "2021-04-05", + "name": "Annar í páskum" + }, + { + "date": "2021-04-22", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2021-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2021-05-13", + "name": "Uppstigningardagur" + }, + { + "date": "2021-05-23", + "name": "Hvítasunnudagur" + }, + { + "date": "2021-05-24", + "name": "Annar í hvítasunnu" + }, + { + "date": "2021-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2021-08-02", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2021-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2021-12-25", + "name": "Jóladagur" + }, + { + "date": "2021-12-26", + "name": "Annar í jólum" + }, + { + "date": "2021-12-31", + "name": "Gamlársdagur" + } + ] + }, + "IT": { + "name": "Italia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Capodanno" + }, + { + "date": "2018-01-06", + "name": "Befana" + }, + { + "date": "2018-04-01", + "name": "Domenica di Pasqua" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2018-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2018-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2018-08-15", + "name": "Ferragosto" + }, + { + "date": "2018-11-01", + "name": "Ognissanti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Capodanno" + }, + { + "date": "2019-01-06", + "name": "Befana" + }, + { + "date": "2019-04-21", + "name": "Domenica di Pasqua" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2019-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2019-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2019-08-15", + "name": "Ferragosto" + }, + { + "date": "2019-11-01", + "name": "Ognissanti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Capodanno" + }, + { + "date": "2020-01-06", + "name": "Befana" + }, + { + "date": "2020-04-12", + "name": "Domenica di Pasqua" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2020-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2020-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2020-08-15", + "name": "Ferragosto" + }, + { + "date": "2020-11-01", + "name": "Ognissanti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Capodanno" + }, + { + "date": "2021-01-06", + "name": "Befana" + }, + { + "date": "2021-04-04", + "name": "Domenica di Pasqua" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2021-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2021-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2021-08-15", + "name": "Ferragosto" + }, + { + "date": "2021-11-01", + "name": "Ognissanti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "JE": { + "name": "Jersey", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-09", + "name": "Liberation Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-09", + "name": "Liberation Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-09", + "name": "Liberation Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-09", + "name": "Liberation Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "JM": { + "name": "Jamaica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-14", + "name": "Ash Wednesday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-23", + "name": "Labour Day" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-06", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "National Heroes Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-06", + "name": "Ash Wednesday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-23", + "name": "Labour Day" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-06", + "name": "Independence Day" + }, + { + "date": "2019-10-21", + "name": "National Heroes Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-26", + "name": "Ash Wednesday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-25", + "name": "Labour Day" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-08-06", + "name": "Independence Day" + }, + { + "date": "2020-10-19", + "name": "National Heroes Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-17", + "name": "Ash Wednesday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-24", + "name": "Labour Day" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-08-06", + "name": "Independence Day" + }, + { + "date": "2021-10-18", + "name": "National Heroes Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "JP": { + "name": "日本", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "元日" + }, + { + "date": "2018-01-02", + "name": "銀行休業日" + }, + { + "date": "2018-01-03", + "name": "銀行休業日" + }, + { + "date": "2018-01-08", + "name": "成人の日" + }, + { + "date": "2018-02-11", + "name": "建国記念の日" + }, + { + "date": "2018-02-12", + "name": "建国記念の日 (代替日)" + }, + { + "date": "2018-03-21", + "name": "春分の日" + }, + { + "date": "2018-04-29", + "name": "昭和の日" + }, + { + "date": "2018-04-30", + "name": "昭和の日 (代替日)" + }, + { + "date": "2018-05-03", + "name": "憲法記念日" + }, + { + "date": "2018-05-04", + "name": "みどりの日" + }, + { + "date": "2018-05-05", + "name": "こどもの日" + }, + { + "date": "2018-07-16", + "name": "海の日" + }, + { + "date": "2018-08-11", + "name": "山の日" + }, + { + "date": "2018-09-17", + "name": "敬老の日" + }, + { + "date": "2018-09-23", + "name": "秋分の日" + }, + { + "date": "2018-09-24", + "name": "秋分の日 (代替日)" + }, + { + "date": "2018-10-08", + "name": "体育の日" + }, + { + "date": "2018-11-03", + "name": "文化の日" + }, + { + "date": "2018-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2018-12-23", + "name": "天皇誕生日" + }, + { + "date": "2018-12-24", + "name": "天皇誕生日 (代替日)" + }, + { + "date": "2018-12-31", + "name": "大晦日" + }, + { + "date": "2019-01-01", + "name": "元日" + }, + { + "date": "2019-01-02", + "name": "銀行休業日" + }, + { + "date": "2019-01-03", + "name": "銀行休業日" + }, + { + "date": "2019-01-14", + "name": "成人の日" + }, + { + "date": "2019-02-11", + "name": "建国記念の日" + }, + { + "date": "2019-03-21", + "name": "春分の日" + }, + { + "date": "2019-04-29", + "name": "昭和の日" + }, + { + "date": "2019-05-03", + "name": "憲法記念日" + }, + { + "date": "2019-05-04", + "name": "みどりの日" + }, + { + "date": "2019-05-05", + "name": "こどもの日" + }, + { + "date": "2019-05-06", + "name": "こどもの日 (代替日)" + }, + { + "date": "2019-07-15", + "name": "海の日" + }, + { + "date": "2019-08-11", + "name": "山の日" + }, + { + "date": "2019-08-12", + "name": "山の日 (代替日)" + }, + { + "date": "2019-09-16", + "name": "敬老の日" + }, + { + "date": "2019-09-23", + "name": "秋分の日" + }, + { + "date": "2019-10-14", + "name": "体育の日" + }, + { + "date": "2019-11-03", + "name": "文化の日" + }, + { + "date": "2019-11-04", + "name": "文化の日 (代替日)" + }, + { + "date": "2019-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2019-12-23", + "name": "天皇誕生日" + }, + { + "date": "2019-12-31", + "name": "大晦日" + }, + { + "date": "2020-01-01", + "name": "元日" + }, + { + "date": "2020-01-02", + "name": "銀行休業日" + }, + { + "date": "2020-01-03", + "name": "銀行休業日" + }, + { + "date": "2020-01-13", + "name": "成人の日" + }, + { + "date": "2020-02-11", + "name": "建国記念の日" + }, + { + "date": "2020-03-20", + "name": "春分の日" + }, + { + "date": "2020-04-29", + "name": "昭和の日" + }, + { + "date": "2020-05-03", + "name": "憲法記念日" + }, + { + "date": "2020-05-04", + "name": "みどりの日" + }, + { + "date": "2020-05-05", + "name": "こどもの日" + }, + { + "date": "2020-05-06", + "name": "憲法記念日 (代替日)" + }, + { + "date": "2020-07-20", + "name": "海の日" + }, + { + "date": "2020-08-11", + "name": "山の日" + }, + { + "date": "2020-09-21", + "name": "敬老の日" + }, + { + "date": "2020-09-22", + "name": "秋分の日" + }, + { + "date": "2020-10-12", + "name": "体育の日" + }, + { + "date": "2020-11-03", + "name": "文化の日" + }, + { + "date": "2020-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2020-12-23", + "name": "天皇誕生日" + }, + { + "date": "2020-12-31", + "name": "大晦日" + }, + { + "date": "2021-01-01", + "name": "元日" + }, + { + "date": "2021-01-02", + "name": "銀行休業日" + }, + { + "date": "2021-01-03", + "name": "銀行休業日" + }, + { + "date": "2021-01-11", + "name": "成人の日" + }, + { + "date": "2021-02-11", + "name": "建国記念の日" + }, + { + "date": "2021-03-20", + "name": "春分の日" + }, + { + "date": "2021-04-29", + "name": "昭和の日" + }, + { + "date": "2021-05-03", + "name": "憲法記念日" + }, + { + "date": "2021-05-04", + "name": "みどりの日" + }, + { + "date": "2021-05-05", + "name": "こどもの日" + }, + { + "date": "2021-07-19", + "name": "海の日" + }, + { + "date": "2021-08-11", + "name": "山の日" + }, + { + "date": "2021-09-20", + "name": "敬老の日" + }, + { + "date": "2021-09-23", + "name": "秋分の日" + }, + { + "date": "2021-10-11", + "name": "体育の日" + }, + { + "date": "2021-11-03", + "name": "文化の日" + }, + { + "date": "2021-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2021-12-23", + "name": "天皇誕生日" + }, + { + "date": "2021-12-31", + "name": "大晦日" + } + ] + }, + "KE": { + "name": "Kenia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2018-03-30", + "name": "Ijumaa Kuu" + }, + { + "date": "2018-04-01", + "name": "Pasaka" + }, + { + "date": "2018-04-02", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2018-06-15", + "name": "Idd-ul-Fitr" + }, + { + "date": "2018-08-21", + "name": "Idd-ul-Azha" + }, + { + "date": "2018-10-10", + "name": "Moi Day" + }, + { + "date": "2018-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2018-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2018-12-25", + "name": "Krismasi" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2019-04-19", + "name": "Ijumaa Kuu" + }, + { + "date": "2019-04-21", + "name": "Pasaka" + }, + { + "date": "2019-04-22", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2019-06-04", + "name": "Idd-ul-Fitr" + }, + { + "date": "2019-08-11", + "name": "Idd-ul-Azha" + }, + { + "date": "2019-10-10", + "name": "Moi Day" + }, + { + "date": "2019-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2019-10-21", + "name": "Siku ya Mashujaa (substitute day)" + }, + { + "date": "2019-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2019-12-25", + "name": "Krismasi" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2020-04-10", + "name": "Ijumaa Kuu" + }, + { + "date": "2020-04-12", + "name": "Pasaka" + }, + { + "date": "2020-04-13", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "Idd-ul-Fitr" + }, + { + "date": "2020-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2020-07-31", + "name": "Idd-ul-Azha" + }, + { + "date": "2020-10-10", + "name": "Moi Day" + }, + { + "date": "2020-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2020-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2020-12-25", + "name": "Krismasi" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2021-04-02", + "name": "Ijumaa Kuu" + }, + { + "date": "2021-04-04", + "name": "Pasaka" + }, + { + "date": "2021-04-05", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Idd-ul-Fitr" + }, + { + "date": "2021-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2021-07-20", + "name": "Idd-ul-Azha" + }, + { + "date": "2021-10-10", + "name": "Moi Day" + }, + { + "date": "2021-10-11", + "name": "Moi Day (substitute day)" + }, + { + "date": "2021-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2021-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2021-12-13", + "name": "Siku ya Jamhuri (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Krismasi" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "KR": { + "name": "대한민국", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "신정" + }, + { + "date": "2018-02-16", + "name": "설날" + }, + { + "date": "2018-03-01", + "name": "3·1절" + }, + { + "date": "2018-05-05", + "name": "어린이날" + }, + { + "date": "2018-05-22", + "name": "석가탄신일" + }, + { + "date": "2018-06-06", + "name": "현충일" + }, + { + "date": "2018-08-15", + "name": "광복절" + }, + { + "date": "2018-09-24", + "name": "추석" + }, + { + "date": "2018-10-03", + "name": "개천절" + }, + { + "date": "2018-10-09", + "name": "한글날" + }, + { + "date": "2018-12-25", + "name": "기독탄신일" + }, + { + "date": "2019-01-01", + "name": "신정" + }, + { + "date": "2019-02-05", + "name": "설날" + }, + { + "date": "2019-03-01", + "name": "3·1절" + }, + { + "date": "2019-05-05", + "name": "어린이날" + }, + { + "date": "2019-05-12", + "name": "석가탄신일" + }, + { + "date": "2019-06-06", + "name": "현충일" + }, + { + "date": "2019-08-15", + "name": "광복절" + }, + { + "date": "2019-09-13", + "name": "추석" + }, + { + "date": "2019-10-03", + "name": "개천절" + }, + { + "date": "2019-10-09", + "name": "한글날" + }, + { + "date": "2019-12-25", + "name": "기독탄신일" + }, + { + "date": "2020-01-01", + "name": "신정" + }, + { + "date": "2020-01-25", + "name": "설날" + }, + { + "date": "2020-03-01", + "name": "3·1절" + }, + { + "date": "2020-04-30", + "name": "석가탄신일" + }, + { + "date": "2020-05-05", + "name": "어린이날" + }, + { + "date": "2020-06-06", + "name": "현충일" + }, + { + "date": "2020-08-15", + "name": "광복절" + }, + { + "date": "2020-10-01", + "name": "추석" + }, + { + "date": "2020-10-03", + "name": "개천절" + }, + { + "date": "2020-10-09", + "name": "한글날" + }, + { + "date": "2020-12-25", + "name": "기독탄신일" + }, + { + "date": "2021-01-01", + "name": "신정" + }, + { + "date": "2021-02-12", + "name": "설날" + }, + { + "date": "2021-03-01", + "name": "3·1절" + }, + { + "date": "2021-05-05", + "name": "어린이날" + }, + { + "date": "2021-05-19", + "name": "석가탄신일" + }, + { + "date": "2021-06-06", + "name": "현충일" + }, + { + "date": "2021-08-15", + "name": "광복절" + }, + { + "date": "2021-09-21", + "name": "추석" + }, + { + "date": "2021-10-03", + "name": "개천절" + }, + { + "date": "2021-10-09", + "name": "한글날" + }, + { + "date": "2021-12-25", + "name": "기독탄신일" + } + ] + }, + "LI": { + "name": "Lichtenstein", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2018-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2018-02-13", + "name": "Faschingsdienstag" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2018-05-10", + "name": "Auffahrt" + }, + { + "date": "2018-05-11", + "name": "Feiertagsbrücke" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-05-31", + "name": "Fronleichnam" + }, + { + "date": "2018-06-01", + "name": "Feiertagsbrücke" + }, + { + "date": "2018-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2018-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "Weihnachten" + }, + { + "date": "2018-12-26", + "name": "Stephanstag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2019-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2019-03-05", + "name": "Faschingsdienstag" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2019-05-30", + "name": "Auffahrt" + }, + { + "date": "2019-05-31", + "name": "Feiertagsbrücke" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-06-20", + "name": "Fronleichnam" + }, + { + "date": "2019-06-21", + "name": "Feiertagsbrücke" + }, + { + "date": "2019-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2019-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "Weihnachten" + }, + { + "date": "2019-12-26", + "name": "Stephanstag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2020-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2020-02-25", + "name": "Faschingsdienstag" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2020-05-21", + "name": "Auffahrt" + }, + { + "date": "2020-05-22", + "name": "Feiertagsbrücke" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-06-11", + "name": "Fronleichnam" + }, + { + "date": "2020-06-12", + "name": "Feiertagsbrücke" + }, + { + "date": "2020-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2020-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "Weihnachten" + }, + { + "date": "2020-12-26", + "name": "Stephanstag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2021-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2021-02-16", + "name": "Faschingsdienstag" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2021-05-13", + "name": "Auffahrt" + }, + { + "date": "2021-05-14", + "name": "Feiertagsbrücke" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-06-03", + "name": "Fronleichnam" + }, + { + "date": "2021-06-04", + "name": "Feiertagsbrücke" + }, + { + "date": "2021-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2021-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "Weihnachten" + }, + { + "date": "2021-12-26", + "name": "Stephanstag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "LT": { + "name": "Lietuva", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Naujieji metai" + }, + { + "date": "2018-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2018-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2018-04-01", + "name": "Velykos" + }, + { + "date": "2018-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2018-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2018-07-06", + "name": "Valstybės diena" + }, + { + "date": "2018-08-15", + "name": "Žolinė" + }, + { + "date": "2018-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2018-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2018-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2018-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2019-01-01", + "name": "Naujieji metai" + }, + { + "date": "2019-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2019-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2019-04-21", + "name": "Velykos" + }, + { + "date": "2019-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2019-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2019-07-06", + "name": "Valstybės diena" + }, + { + "date": "2019-08-15", + "name": "Žolinė" + }, + { + "date": "2019-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2019-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2019-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2019-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2020-01-01", + "name": "Naujieji metai" + }, + { + "date": "2020-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2020-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2020-04-12", + "name": "Velykos" + }, + { + "date": "2020-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2020-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2020-07-06", + "name": "Valstybės diena" + }, + { + "date": "2020-08-15", + "name": "Žolinė" + }, + { + "date": "2020-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2020-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2020-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2020-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2021-01-01", + "name": "Naujieji metai" + }, + { + "date": "2021-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2021-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2021-04-04", + "name": "Velykos" + }, + { + "date": "2021-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2021-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2021-07-06", + "name": "Valstybės diena" + }, + { + "date": "2021-08-15", + "name": "Žolinė" + }, + { + "date": "2021-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2021-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2021-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2021-12-26", + "name": "2. Kalėdų diena" + } + ] + }, + "LU": { + "name": "Luxembourg", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "1er mai" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "1er mai" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "1er mai" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "1er mai" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "LV": { + "name": "Latvija", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2018-03-30", + "name": "Lielā Piektdiena" + }, + { + "date": "2018-04-01", + "name": "Lieldienas" + }, + { + "date": "2018-04-02", + "name": "Otrās Lieldienas" + }, + { + "date": "2018-05-01", + "name": "Darba svētki" + }, + { + "date": "2018-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2018-06-23", + "name": "Līgo Diena" + }, + { + "date": "2018-06-24", + "name": "Jāņi" + }, + { + "date": "2018-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2018-11-19", + "name": "Latvijas Republikas proklamēšanas diena (aizstājējs diena)" + }, + { + "date": "2018-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2018-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2018-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2018-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2019-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2019-04-19", + "name": "Lielā Piektdiena" + }, + { + "date": "2019-04-21", + "name": "Lieldienas" + }, + { + "date": "2019-04-22", + "name": "Otrās Lieldienas" + }, + { + "date": "2019-05-01", + "name": "Darba svētki" + }, + { + "date": "2019-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2019-05-06", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena (aizstājējs diena)" + }, + { + "date": "2019-06-23", + "name": "Līgo Diena" + }, + { + "date": "2019-06-24", + "name": "Jāņi" + }, + { + "date": "2019-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2019-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2019-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2019-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2019-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2020-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2020-04-10", + "name": "Lielā Piektdiena" + }, + { + "date": "2020-04-12", + "name": "Lieldienas" + }, + { + "date": "2020-04-13", + "name": "Otrās Lieldienas" + }, + { + "date": "2020-05-01", + "name": "Darba svētki" + }, + { + "date": "2020-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2020-06-23", + "name": "Līgo Diena" + }, + { + "date": "2020-06-24", + "name": "Jāņi" + }, + { + "date": "2020-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2020-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2020-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2020-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2020-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2021-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2021-04-02", + "name": "Lielā Piektdiena" + }, + { + "date": "2021-04-04", + "name": "Lieldienas" + }, + { + "date": "2021-04-05", + "name": "Otrās Lieldienas" + }, + { + "date": "2021-05-01", + "name": "Darba svētki" + }, + { + "date": "2021-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2021-06-23", + "name": "Līgo Diena" + }, + { + "date": "2021-06-24", + "name": "Jāņi" + }, + { + "date": "2021-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2021-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2021-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2021-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2021-12-31", + "name": "Vecgada vakars" + } + ] + }, + "MC": { + "name": "Monaco", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "1er mai" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-31", + "name": "la Fête-Dieu" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2018-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2019-01-28", + "name": "Sainte Dévote (jour substitut)" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "1er mai" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-20", + "name": "la Fête-Dieu" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2019-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "1er mai" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-11", + "name": "la Fête-Dieu" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2020-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "1er mai" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-03", + "name": "la Fête-Dieu" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2021-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "MD": { + "name": "Republica Moldova", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Anul nou" + }, + { + "date": "2018-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2018-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2018-04-08", + "name": "Paștele" + }, + { + "date": "2018-04-09", + "name": "Două zi de Pasti" + }, + { + "date": "2018-04-16", + "name": "Paştele Blăjinilor" + }, + { + "date": "2018-05-01", + "name": "Ziua muncii" + }, + { + "date": "2018-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2018-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2018-08-31", + "name": "Limba noastră" + }, + { + "date": "2018-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2019-01-01", + "name": "Anul nou" + }, + { + "date": "2019-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2019-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2019-04-28", + "name": "Paștele" + }, + { + "date": "2019-04-29", + "name": "Două zi de Pasti" + }, + { + "date": "2019-05-01", + "name": "Ziua muncii" + }, + { + "date": "2019-05-06", + "name": "Paştele Blăjinilor" + }, + { + "date": "2019-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2019-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2019-08-31", + "name": "Limba noastră" + }, + { + "date": "2019-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2020-01-01", + "name": "Anul nou" + }, + { + "date": "2020-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2020-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2020-04-19", + "name": "Paștele" + }, + { + "date": "2020-04-20", + "name": "Două zi de Pasti" + }, + { + "date": "2020-04-27", + "name": "Paştele Blăjinilor" + }, + { + "date": "2020-05-01", + "name": "Ziua muncii" + }, + { + "date": "2020-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2020-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2020-08-31", + "name": "Limba noastră" + }, + { + "date": "2020-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2021-01-01", + "name": "Anul nou" + }, + { + "date": "2021-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2021-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2021-05-01", + "name": "Ziua muncii" + }, + { + "date": "2021-05-02", + "name": "Paștele" + }, + { + "date": "2021-05-03", + "name": "Două zi de Pasti" + }, + { + "date": "2021-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2021-05-10", + "name": "Paştele Blăjinilor" + }, + { + "date": "2021-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2021-08-31", + "name": "Limba noastră" + }, + { + "date": "2021-12-25", + "name": "Craciun pe stil Nou" + } + ] + }, + "ME": { + "name": "Crna Gora", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nova godina" + }, + { + "date": "2018-01-02", + "name": "Nova godina" + }, + { + "date": "2018-01-06", + "name": "Badnji dan" + }, + { + "date": "2018-01-07", + "name": "Božić" + }, + { + "date": "2018-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2018-03-30", + "name": "Veliki petak" + }, + { + "date": "2018-03-31", + "name": "Pesač" + }, + { + "date": "2018-04-01", + "name": "Uskrs" + }, + { + "date": "2018-04-02", + "name": "Uskrs" + }, + { + "date": "2018-04-06", + "name": "Veliki petak" + }, + { + "date": "2018-04-08", + "name": "Uskrs" + }, + { + "date": "2018-05-01", + "name": "Praznik rada" + }, + { + "date": "2018-05-02", + "name": "Praznik rada" + }, + { + "date": "2018-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2018-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2018-06-15", + "name": "Ramazanski bajram" + }, + { + "date": "2018-07-13", + "name": "Dan državnosti" + }, + { + "date": "2018-07-14", + "name": "Dan državnosti" + }, + { + "date": "2018-08-21", + "name": "Kurban-bajram" + }, + { + "date": "2018-09-19", + "name": "Jom Kipur" + }, + { + "date": "2018-11-01", + "name": "Svi sveti" + }, + { + "date": "2018-12-24", + "name": "Badnji dan" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Božić" + }, + { + "date": "2019-01-01", + "name": "Nova godina" + }, + { + "date": "2019-01-02", + "name": "Nova godina" + }, + { + "date": "2019-01-06", + "name": "Badnji dan" + }, + { + "date": "2019-01-07", + "name": "Božić" + }, + { + "date": "2019-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2019-04-19", + "name": "Veliki petak" + }, + { + "date": "2019-04-20", + "name": "Pesač" + }, + { + "date": "2019-04-21", + "name": "Uskrs" + }, + { + "date": "2019-04-22", + "name": "Uskrs" + }, + { + "date": "2019-04-26", + "name": "Veliki petak" + }, + { + "date": "2019-04-28", + "name": "Uskrs" + }, + { + "date": "2019-05-01", + "name": "Praznik rada" + }, + { + "date": "2019-05-02", + "name": "Praznik rada" + }, + { + "date": "2019-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2019-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2019-06-04", + "name": "Ramazanski bajram" + }, + { + "date": "2019-07-13", + "name": "Dan državnosti" + }, + { + "date": "2019-07-14", + "name": "Dan državnosti" + }, + { + "date": "2019-07-15", + "name": "Dan državnosti (zamjena dan)" + }, + { + "date": "2019-08-11", + "name": "Kurban-bajram" + }, + { + "date": "2019-10-09", + "name": "Jom Kipur" + }, + { + "date": "2019-11-01", + "name": "Svi sveti" + }, + { + "date": "2019-12-24", + "name": "Badnji dan" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Božić" + }, + { + "date": "2020-01-01", + "name": "Nova godina" + }, + { + "date": "2020-01-02", + "name": "Nova godina" + }, + { + "date": "2020-01-06", + "name": "Badnji dan" + }, + { + "date": "2020-01-07", + "name": "Božić" + }, + { + "date": "2020-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2020-04-09", + "name": "Pesač" + }, + { + "date": "2020-04-10", + "name": "Veliki petak" + }, + { + "date": "2020-04-12", + "name": "Uskrs" + }, + { + "date": "2020-04-13", + "name": "Uskrs" + }, + { + "date": "2020-04-17", + "name": "Veliki petak" + }, + { + "date": "2020-04-19", + "name": "Uskrs" + }, + { + "date": "2020-05-01", + "name": "Praznik rada" + }, + { + "date": "2020-05-02", + "name": "Praznik rada" + }, + { + "date": "2020-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2020-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2020-05-24", + "name": "Ramazanski bajram" + }, + { + "date": "2020-07-13", + "name": "Dan državnosti" + }, + { + "date": "2020-07-14", + "name": "Dan državnosti" + }, + { + "date": "2020-07-31", + "name": "Kurban-bajram" + }, + { + "date": "2020-09-28", + "name": "Jom Kipur" + }, + { + "date": "2020-11-01", + "name": "Svi sveti" + }, + { + "date": "2020-12-24", + "name": "Badnji dan" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Božić" + }, + { + "date": "2021-01-01", + "name": "Nova godina" + }, + { + "date": "2021-01-02", + "name": "Nova godina" + }, + { + "date": "2021-01-06", + "name": "Badnji dan" + }, + { + "date": "2021-01-07", + "name": "Božić" + }, + { + "date": "2021-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2021-03-28", + "name": "Pesač" + }, + { + "date": "2021-04-02", + "name": "Veliki petak" + }, + { + "date": "2021-04-04", + "name": "Uskrs" + }, + { + "date": "2021-04-05", + "name": "Uskrs" + }, + { + "date": "2021-04-30", + "name": "Veliki petak" + }, + { + "date": "2021-05-01", + "name": "Praznik rada" + }, + { + "date": "2021-05-02", + "name": "Uskrs" + }, + { + "date": "2021-05-02", + "name": "Praznik rada" + }, + { + "date": "2021-05-03", + "name": "Praznik rada (zamjena dan)" + }, + { + "date": "2021-05-13", + "name": "Ramazanski bajram" + }, + { + "date": "2021-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2021-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2021-07-13", + "name": "Dan državnosti" + }, + { + "date": "2021-07-14", + "name": "Dan državnosti" + }, + { + "date": "2021-07-20", + "name": "Kurban-bajram" + }, + { + "date": "2021-09-16", + "name": "Jom Kipur" + }, + { + "date": "2021-11-01", + "name": "Svi sveti" + }, + { + "date": "2021-12-24", + "name": "Badnji dan" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Božić" + } + ] + }, + "MG": { + "name": "Repoblikan'i Madagasikara", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Taom-baovao" + }, + { + "date": "2018-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2018-04-02", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2018-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2018-05-10", + "name": "Andro niakarana" + }, + { + "date": "2018-05-21", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2018-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2018-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2018-08-15", + "name": "Asompsiona" + }, + { + "date": "2018-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2018-12-25", + "name": "Krismasy" + }, + { + "date": "2019-01-01", + "name": "Taom-baovao" + }, + { + "date": "2019-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2019-04-22", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2019-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2019-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2019-05-30", + "name": "Andro niakarana" + }, + { + "date": "2019-06-10", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2019-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2019-08-15", + "name": "Asompsiona" + }, + { + "date": "2019-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2019-12-25", + "name": "Krismasy" + }, + { + "date": "2020-01-01", + "name": "Taom-baovao" + }, + { + "date": "2020-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2020-04-13", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2020-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2020-05-21", + "name": "Andro niakarana" + }, + { + "date": "2020-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2020-06-01", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2020-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2020-08-15", + "name": "Asompsiona" + }, + { + "date": "2020-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2020-12-25", + "name": "Krismasy" + }, + { + "date": "2021-01-01", + "name": "Taom-baovao" + }, + { + "date": "2021-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2021-04-05", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2021-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2021-05-13", + "name": "Andro niakarana" + }, + { + "date": "2021-05-24", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2021-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2021-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2021-08-15", + "name": "Asompsiona" + }, + { + "date": "2021-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2021-12-25", + "name": "Krismasy" + } + ] + }, + "MK": { + "name": "Република Македонија", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова Година" + }, + { + "date": "2018-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2018-01-08", + "name": "Прв ден Божик (заменет ден)" + }, + { + "date": "2018-04-08", + "name": "Прв ден Велигден" + }, + { + "date": "2018-04-09", + "name": "Втор ден Велигден" + }, + { + "date": "2018-05-01", + "name": "Ден на трудот" + }, + { + "date": "2018-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2018-06-15", + "name": "Рамазан Бајрам" + }, + { + "date": "2018-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2018-09-08", + "name": "Ден на независноста" + }, + { + "date": "2018-10-11", + "name": "Ден на востанието" + }, + { + "date": "2018-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2018-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2019-01-01", + "name": "Нова Година" + }, + { + "date": "2019-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2019-04-28", + "name": "Прв ден Велигден" + }, + { + "date": "2019-04-29", + "name": "Втор ден Велигден" + }, + { + "date": "2019-05-01", + "name": "Ден на трудот" + }, + { + "date": "2019-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2019-06-04", + "name": "Рамазан Бајрам" + }, + { + "date": "2019-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2019-09-08", + "name": "Ден на независноста" + }, + { + "date": "2019-09-09", + "name": "Ден на независноста (заменет ден)" + }, + { + "date": "2019-10-11", + "name": "Ден на востанието" + }, + { + "date": "2019-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2019-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2019-12-09", + "name": "Св. Климент Охридски (заменет ден)" + }, + { + "date": "2020-01-01", + "name": "Нова Година" + }, + { + "date": "2020-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2020-04-19", + "name": "Прв ден Велигден" + }, + { + "date": "2020-04-20", + "name": "Втор ден Велигден" + }, + { + "date": "2020-05-01", + "name": "Ден на трудот" + }, + { + "date": "2020-05-24", + "name": "Рамазан Бајрам" + }, + { + "date": "2020-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2020-05-25", + "name": "Рамазан Бајрам (заменет ден)" + }, + { + "date": "2020-05-25", + "name": "Св. Кирил и Методиј (заменет ден)" + }, + { + "date": "2020-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2020-08-03", + "name": "Ден на Републиката (заменет ден)" + }, + { + "date": "2020-09-08", + "name": "Ден на независноста" + }, + { + "date": "2020-10-11", + "name": "Ден на востанието" + }, + { + "date": "2020-10-12", + "name": "Ден на востанието (заменет ден)" + }, + { + "date": "2020-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2020-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2021-01-01", + "name": "Нова Година" + }, + { + "date": "2021-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2021-05-01", + "name": "Ден на трудот" + }, + { + "date": "2021-05-02", + "name": "Прв ден Велигден" + }, + { + "date": "2021-05-03", + "name": "Втор ден Велигден" + }, + { + "date": "2021-05-13", + "name": "Рамазан Бајрам" + }, + { + "date": "2021-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2021-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2021-09-08", + "name": "Ден на независноста" + }, + { + "date": "2021-10-11", + "name": "Ден на востанието" + }, + { + "date": "2021-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2021-12-08", + "name": "Св. Климент Охридски" + } + ] + }, + "MQ": { + "name": "Martinique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "MT": { + "name": "Malta", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2018-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2018-03-19", + "name": "San Ġużepp" + }, + { + "date": "2018-03-30", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2018-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2018-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2018-06-07", + "name": "Sette Giugno" + }, + { + "date": "2018-06-29", + "name": "L-Imnarja" + }, + { + "date": "2018-08-15", + "name": "Santa Marija" + }, + { + "date": "2018-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2018-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2018-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2018-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2018-12-25", + "name": "Il-Milied" + }, + { + "date": "2019-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2019-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2019-03-19", + "name": "San Ġużepp" + }, + { + "date": "2019-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2019-04-19", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2019-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2019-06-07", + "name": "Sette Giugno" + }, + { + "date": "2019-06-29", + "name": "L-Imnarja" + }, + { + "date": "2019-08-15", + "name": "Santa Marija" + }, + { + "date": "2019-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2019-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2019-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2019-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2019-12-25", + "name": "Il-Milied" + }, + { + "date": "2020-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2020-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2020-03-19", + "name": "San Ġużepp" + }, + { + "date": "2020-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2020-04-10", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2020-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2020-06-07", + "name": "Sette Giugno" + }, + { + "date": "2020-06-29", + "name": "L-Imnarja" + }, + { + "date": "2020-08-15", + "name": "Santa Marija" + }, + { + "date": "2020-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2020-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2020-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2020-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2020-12-25", + "name": "Il-Milied" + }, + { + "date": "2021-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2021-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2021-03-19", + "name": "San Ġużepp" + }, + { + "date": "2021-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2021-04-02", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2021-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2021-06-07", + "name": "Sette Giugno" + }, + { + "date": "2021-06-29", + "name": "L-Imnarja" + }, + { + "date": "2021-08-15", + "name": "Santa Marija" + }, + { + "date": "2021-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2021-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2021-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2021-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2021-12-25", + "name": "Il-Milied" + } + ] + }, + "MW": { + "name": "Malawi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2018-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2018-03-05", + "name": "Martyrs' Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2018-06-15", + "name": "Eid al Fitri" + }, + { + "date": "2018-07-06", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "Mother's Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2019-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2019-03-04", + "name": "Martyrs' Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2019-06-04", + "name": "Eid al Fitri" + }, + { + "date": "2019-07-06", + "name": "Independence Day" + }, + { + "date": "2019-07-08", + "name": "Independence Day (substitute day)" + }, + { + "date": "2019-10-15", + "name": "Mother's Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2020-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2020-05-24", + "name": "Eid al Fitri" + }, + { + "date": "2020-05-25", + "name": "Eid al Fitri (substitute day)" + }, + { + "date": "2020-07-06", + "name": "Independence Day" + }, + { + "date": "2020-10-15", + "name": "Mother's Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2021-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-03", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Eid al Fitri" + }, + { + "date": "2021-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2021-07-06", + "name": "Independence Day" + }, + { + "date": "2021-10-15", + "name": "Mother's Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + } + ] + }, + "MX": { + "name": "México", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-05", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2018-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2018-03-19", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2018-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2018-11-19", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2018-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2018-12-01", + "name": "Transmisión del Poder Ejecutivo Federal" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-02-04", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2019-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2019-03-18", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2019-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2019-11-18", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2019-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-03", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2020-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2020-03-16", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2020-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2020-11-16", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2020-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-01", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2021-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2021-03-15", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2021-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-15", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2021-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "MZ": { + "name": "Moçambique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2018-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2018-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2018-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2018-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2018-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2018-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2018-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2018-12-25", + "name": "Dia da Família" + }, + { + "date": "2019-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2019-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2019-02-04", + "name": "Feriado Obrigatório" + }, + { + "date": "2019-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2019-04-08", + "name": "Feriado Obrigatório" + }, + { + "date": "2019-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2019-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2019-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2019-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2019-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2019-12-25", + "name": "Dia da Família" + }, + { + "date": "2020-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2020-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2020-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2020-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2020-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2020-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2020-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2020-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2020-10-05", + "name": "Feriado Obrigatório" + }, + { + "date": "2020-12-25", + "name": "Dia da Família" + }, + { + "date": "2021-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2021-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2021-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2021-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2021-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2021-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2021-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2021-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2021-12-25", + "name": "Dia da Família" + } + ] + }, + "NA": { + "name": "Namibia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-21", + "name": "Independence Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Workers Day" + }, + { + "date": "2018-05-04", + "name": "Cassinga Day" + }, + { + "date": "2018-05-10", + "name": "Ascension Day" + }, + { + "date": "2018-05-25", + "name": "Africa Day" + }, + { + "date": "2018-08-26", + "name": "Heroes' Day" + }, + { + "date": "2018-08-27", + "name": "Public Holiday" + }, + { + "date": "2018-12-10", + "name": "Human Rights Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-21", + "name": "Independence Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Workers Day" + }, + { + "date": "2019-05-04", + "name": "Cassinga Day" + }, + { + "date": "2019-05-25", + "name": "Africa Day" + }, + { + "date": "2019-05-30", + "name": "Ascension Day" + }, + { + "date": "2019-08-26", + "name": "Heroes' Day" + }, + { + "date": "2019-12-10", + "name": "Human Rights Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-21", + "name": "Independence Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Workers Day" + }, + { + "date": "2020-05-04", + "name": "Cassinga Day" + }, + { + "date": "2020-05-21", + "name": "Ascension Day" + }, + { + "date": "2020-05-25", + "name": "Africa Day" + }, + { + "date": "2020-08-26", + "name": "Heroes' Day" + }, + { + "date": "2020-12-10", + "name": "Human Rights Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-21", + "name": "Independence Day" + }, + { + "date": "2021-03-22", + "name": "Public Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Workers Day" + }, + { + "date": "2021-05-04", + "name": "Cassinga Day" + }, + { + "date": "2021-05-13", + "name": "Ascension Day" + }, + { + "date": "2021-05-25", + "name": "Africa Day" + }, + { + "date": "2021-08-26", + "name": "Heroes' Day" + }, + { + "date": "2021-12-10", + "name": "Human Rights Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" + } + ] + }, + "NI": { + "name": "Nicaragua", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-01", + "name": "Día del niño" + }, + { + "date": "2018-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2018-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-01", + "name": "Día del niño" + }, + { + "date": "2019-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2019-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-01", + "name": "Día del niño" + }, + { + "date": "2020-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2020-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-01", + "name": "Día del niño" + }, + { + "date": "2021-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2021-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "NL": { + "name": "Nederland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2018-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2018-05-10", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2018-05-21", + "name": "Pinkstermaandag" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2018-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2019-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2019-05-30", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2019-06-10", + "name": "Pinkstermaandag" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2020-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2020-05-21", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2020-06-01", + "name": "Pinkstermaandag" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2021-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2021-05-13", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2021-05-24", + "name": "Pinkstermaandag" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-12-31", + "name": "Oudejaarsavond" + } + ] + }, + "NO": { + "name": "Norge", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2018-03-29", + "name": "Skjærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Første påskedag" + }, + { + "date": "2018-04-02", + "name": "Andre påskedag" + }, + { + "date": "2018-05-01", + "name": "Første mai" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2018-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2018-05-20", + "name": "Første pinsedag" + }, + { + "date": "2018-05-21", + "name": "Andre pinsedag" + }, + { + "date": "2018-12-24", + "name": "Julaften" + }, + { + "date": "2018-12-25", + "name": "Første Juledag" + }, + { + "date": "2018-12-26", + "name": "Andre juledag" + }, + { + "date": "2018-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2019-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2019-04-18", + "name": "Skjærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Første påskedag" + }, + { + "date": "2019-04-22", + "name": "Andre påskedag" + }, + { + "date": "2019-05-01", + "name": "Første mai" + }, + { + "date": "2019-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Første pinsedag" + }, + { + "date": "2019-06-10", + "name": "Andre pinsedag" + }, + { + "date": "2019-12-24", + "name": "Julaften" + }, + { + "date": "2019-12-25", + "name": "Første Juledag" + }, + { + "date": "2019-12-26", + "name": "Andre juledag" + }, + { + "date": "2019-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2020-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2020-04-09", + "name": "Skjærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Første påskedag" + }, + { + "date": "2020-04-13", + "name": "Andre påskedag" + }, + { + "date": "2020-05-01", + "name": "Første mai" + }, + { + "date": "2020-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Første pinsedag" + }, + { + "date": "2020-06-01", + "name": "Andre pinsedag" + }, + { + "date": "2020-12-24", + "name": "Julaften" + }, + { + "date": "2020-12-25", + "name": "Første Juledag" + }, + { + "date": "2020-12-26", + "name": "Andre juledag" + }, + { + "date": "2020-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2021-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2021-04-01", + "name": "Skjærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Første påskedag" + }, + { + "date": "2021-04-05", + "name": "Andre påskedag" + }, + { + "date": "2021-05-01", + "name": "Første mai" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2021-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2021-05-23", + "name": "Første pinsedag" + }, + { + "date": "2021-05-24", + "name": "Andre pinsedag" + }, + { + "date": "2021-12-24", + "name": "Julaften" + }, + { + "date": "2021-12-25", + "name": "Første Juledag" + }, + { + "date": "2021-12-26", + "name": "Andre juledag" + }, + { + "date": "2021-12-31", + "name": "Nyttårsaften" + } + ] + }, + "NZ": { + "name": "New Zealand", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2018-02-06", + "name": "Waitangi Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-25", + "name": "ANZAC Day" + }, + { + "date": "2018-06-04", + "name": "Queen's Birthday" + }, + { + "date": "2018-10-22", + "name": "Labour Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2019-02-06", + "name": "Waitangi Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "ANZAC Day" + }, + { + "date": "2019-06-03", + "name": "Queen's Birthday" + }, + { + "date": "2019-10-28", + "name": "Labour Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2020-02-06", + "name": "Waitangi Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-27", + "name": "ANZAC Day" + }, + { + "date": "2020-06-01", + "name": "Queen's Birthday" + }, + { + "date": "2020-10-26", + "name": "Labour Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2021-01-04", + "name": "Day after New Year's Day (substitute day)" + }, + { + "date": "2021-02-08", + "name": "Waitangi Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-26", + "name": "ANZAC Day" + }, + { + "date": "2021-06-07", + "name": "Queen's Birthday" + }, + { + "date": "2021-10-25", + "name": "Labour Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "PA": { + "name": "Panamá", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2018-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2018-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-08", + "name": "Día de la Madre" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-01", + "name": "Presidential Inauguration" + }, + { + "date": "2019-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2019-11-04", + "name": "Día de la Separación (de Colombia) (día sustituto)" + }, + { + "date": "2019-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2019-11-11", + "name": "Primer Grito de Independencia (día sustituto)" + }, + { + "date": "2019-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-08", + "name": "Día de la Madre" + }, + { + "date": "2019-12-09", + "name": "Día de la Madre (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2020-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2020-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-08", + "name": "Día de la Madre" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2021-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2021-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-29", + "name": "Día de la Independencia (día sustituto)" + }, + { + "date": "2021-12-08", + "name": "Día de la Madre" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "PE": { + "name": "Perú", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2018-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2018-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2019-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2019-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2020-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2020-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2021-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2021-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "PL": { + "name": "Polska", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nowy Rok" + }, + { + "date": "2018-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2018-04-01", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2018-04-02", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2018-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2018-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2018-05-20", + "name": "Zielone Świątki" + }, + { + "date": "2018-05-31", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2018-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2018-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2018-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2018-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2018-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2019-01-01", + "name": "Nowy Rok" + }, + { + "date": "2019-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2019-04-21", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2019-04-22", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2019-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2019-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2019-06-09", + "name": "Zielone Świątki" + }, + { + "date": "2019-06-20", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2019-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2019-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2019-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2019-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2019-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2020-01-01", + "name": "Nowy Rok" + }, + { + "date": "2020-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2020-04-12", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2020-04-13", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2020-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2020-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2020-05-31", + "name": "Zielone Świątki" + }, + { + "date": "2020-06-11", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2020-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2020-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2020-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2020-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2020-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2021-01-01", + "name": "Nowy Rok" + }, + { + "date": "2021-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2021-04-04", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2021-04-05", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2021-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2021-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2021-05-23", + "name": "Zielone Świątki" + }, + { + "date": "2021-06-03", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2021-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2021-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2021-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2021-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2021-12-26", + "name": "Drugi dzień Bożego Narodzenia" + } + ] + }, + "PT": { + "name": "Portugal", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-01", + "name": "Páscoa" + }, + { + "date": "2018-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-05-31", + "name": "Corpo de Deus" + }, + { + "date": "2018-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2018-08-15", + "name": "Assunção" + }, + { + "date": "2018-10-05", + "name": "Implantação da República" + }, + { + "date": "2018-11-01", + "name": "Todos os santos" + }, + { + "date": "2018-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2018-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-04-21", + "name": "Páscoa" + }, + { + "date": "2019-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2019-06-20", + "name": "Corpo de Deus" + }, + { + "date": "2019-08-15", + "name": "Assunção" + }, + { + "date": "2019-10-05", + "name": "Implantação da República" + }, + { + "date": "2019-11-01", + "name": "Todos os santos" + }, + { + "date": "2019-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2019-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-04-12", + "name": "Páscoa" + }, + { + "date": "2020-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2020-06-11", + "name": "Corpo de Deus" + }, + { + "date": "2020-08-15", + "name": "Assunção" + }, + { + "date": "2020-10-05", + "name": "Implantação da República" + }, + { + "date": "2020-11-01", + "name": "Todos os santos" + }, + { + "date": "2020-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2020-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-04", + "name": "Páscoa" + }, + { + "date": "2021-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-06-03", + "name": "Corpo de Deus" + }, + { + "date": "2021-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2021-08-15", + "name": "Assunção" + }, + { + "date": "2021-10-05", + "name": "Implantação da República" + }, + { + "date": "2021-11-01", + "name": "Todos os santos" + }, + { + "date": "2021-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2021-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2021-12-25", + "name": "Natal" + }, + { + "date": "2022-01-01", + "name": "Ano Novo" + }, + { + "date": "2022-04-15", + "name": "Sexta-Feira Santa" + }, + { + "date": "2022-04-17", + "name": "Páscoa" + }, + { + "date": "2022-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2022-05-01", + "name": "Dia do Trabalhador" + }, + { + "date": "2022-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2022-06-16", + "name": "Corpo de Deus" + }, + { + "date": "2022-08-15", + "name": "Assunção de Nossa Senhora" + }, + { + "date": "2022-10-05", + "name": "Implantação da República" + }, + { + "date": "2022-11-01", + "name": "Dia de Todos os Santos" + }, + { + "date": "2022-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2022-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2022-12-25", + "name": "Natal" + } + ] + }, + "PY": { + "name": "Paraguay", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2018-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-06-18", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2018-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2019-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-06-17", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2019-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2020-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-06-15", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2020-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2021-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-06-14", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2021-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "RE": { + "name": "Réunion", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "RO": { + "name": "Romania", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Anul nou" + }, + { + "date": "2018-04-08", + "name": "Paștele" + }, + { + "date": "2018-04-09", + "name": "Două zi de Pasti" + }, + { + "date": "2018-05-01", + "name": "Ziua muncii" + }, + { + "date": "2018-05-27", + "name": "Rusaliile" + }, + { + "date": "2018-05-28", + "name": "Două zi de Rusalii" + }, + { + "date": "2018-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2018-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2018-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2018-12-25", + "name": "Crăciunul" + }, + { + "date": "2018-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2019-01-01", + "name": "Anul nou" + }, + { + "date": "2019-04-28", + "name": "Paștele" + }, + { + "date": "2019-04-29", + "name": "Două zi de Pasti" + }, + { + "date": "2019-05-01", + "name": "Ziua muncii" + }, + { + "date": "2019-06-16", + "name": "Rusaliile" + }, + { + "date": "2019-06-17", + "name": "Două zi de Rusalii" + }, + { + "date": "2019-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2019-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2019-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2019-12-25", + "name": "Crăciunul" + }, + { + "date": "2019-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2020-01-01", + "name": "Anul nou" + }, + { + "date": "2020-04-19", + "name": "Paștele" + }, + { + "date": "2020-04-20", + "name": "Două zi de Pasti" + }, + { + "date": "2020-05-01", + "name": "Ziua muncii" + }, + { + "date": "2020-06-07", + "name": "Rusaliile" + }, + { + "date": "2020-06-08", + "name": "Două zi de Rusalii" + }, + { + "date": "2020-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2020-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2020-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2020-12-25", + "name": "Crăciunul" + }, + { + "date": "2020-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2021-01-01", + "name": "Anul nou" + }, + { + "date": "2021-05-01", + "name": "Ziua muncii" + }, + { + "date": "2021-05-02", + "name": "Paștele" + }, + { + "date": "2021-05-03", + "name": "Două zi de Pasti" + }, + { + "date": "2021-06-20", + "name": "Rusaliile" + }, + { + "date": "2021-06-21", + "name": "Două zi de Rusalii" + }, + { + "date": "2021-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2021-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2021-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2021-12-25", + "name": "Crăciunul" + }, + { + "date": "2021-12-26", + "name": "Două zi de Crăciun" + } + ] + }, + "RS": { + "name": "Република Србија", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова година" + }, + { + "date": "2018-01-02", + "name": "Нова година" + }, + { + "date": "2018-01-07", + "name": "Божић" + }, + { + "date": "2018-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2018-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2018-04-06", + "name": "Велики петак" + }, + { + "date": "2018-04-08", + "name": "Васкрс" + }, + { + "date": "2018-04-09", + "name": "Васкрсни понедељак" + }, + { + "date": "2018-05-01", + "name": "Празник рада" + }, + { + "date": "2018-05-02", + "name": "Празник рада" + }, + { + "date": "2018-11-12", + "name": "Дан примирја" + }, + { + "date": "2019-01-01", + "name": "Нова година" + }, + { + "date": "2019-01-02", + "name": "Нова година" + }, + { + "date": "2019-01-07", + "name": "Божић" + }, + { + "date": "2019-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2019-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2019-04-26", + "name": "Велики петак" + }, + { + "date": "2019-04-28", + "name": "Васкрс" + }, + { + "date": "2019-04-29", + "name": "Васкрсни понедељак" + }, + { + "date": "2019-05-01", + "name": "Празник рада" + }, + { + "date": "2019-05-02", + "name": "Празник рада" + }, + { + "date": "2019-11-11", + "name": "Дан примирја" + }, + { + "date": "2020-01-01", + "name": "Нова година" + }, + { + "date": "2020-01-02", + "name": "Нова година" + }, + { + "date": "2020-01-07", + "name": "Божић" + }, + { + "date": "2020-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2020-02-17", + "name": "Дан државности Србије" + }, + { + "date": "2020-04-17", + "name": "Велики петак" + }, + { + "date": "2020-04-19", + "name": "Васкрс" + }, + { + "date": "2020-04-20", + "name": "Васкрсни понедељак" + }, + { + "date": "2020-05-01", + "name": "Празник рада" + }, + { + "date": "2020-05-02", + "name": "Празник рада" + }, + { + "date": "2020-11-11", + "name": "Дан примирја" + }, + { + "date": "2021-01-01", + "name": "Нова година" + }, + { + "date": "2021-01-02", + "name": "Нова година" + }, + { + "date": "2021-01-07", + "name": "Божић" + }, + { + "date": "2021-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2021-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2021-04-30", + "name": "Велики петак" + }, + { + "date": "2021-05-01", + "name": "Празник рада" + }, + { + "date": "2021-05-02", + "name": "Васкрс" + }, + { + "date": "2021-05-03", + "name": "Празник рада" + }, + { + "date": "2021-05-03", + "name": "Васкрсни понедељак" + }, + { + "date": "2021-11-11", + "name": "Дан примирја" + } + ] + }, + "RU": { + "name": "Россия", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новый год" + }, + { + "date": "2018-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2018-01-07", + "name": "Рождество Христово" + }, + { + "date": "2018-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2018-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2018-03-08", + "name": "Международный женский день" + }, + { + "date": "2018-05-01", + "name": "День весны и труда" + }, + { + "date": "2018-05-09", + "name": "День Победы" + }, + { + "date": "2018-06-12", + "name": "День России" + }, + { + "date": "2018-08-22", + "name": "День Государственного флага" + }, + { + "date": "2018-11-04", + "name": "День народного единства" + }, + { + "date": "2019-01-01", + "name": "Новый год" + }, + { + "date": "2019-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2019-01-07", + "name": "Рождество Христово" + }, + { + "date": "2019-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2019-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2019-03-08", + "name": "Международный женский день" + }, + { + "date": "2019-05-01", + "name": "День весны и труда" + }, + { + "date": "2019-05-09", + "name": "День Победы" + }, + { + "date": "2019-06-12", + "name": "День России" + }, + { + "date": "2019-08-22", + "name": "День Государственного флага" + }, + { + "date": "2019-11-04", + "name": "День народного единства" + }, + { + "date": "2020-01-01", + "name": "Новый год" + }, + { + "date": "2020-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2020-01-07", + "name": "Рождество Христово" + }, + { + "date": "2020-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2020-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2020-03-08", + "name": "Международный женский день" + }, + { + "date": "2020-05-01", + "name": "День весны и труда" + }, + { + "date": "2020-05-09", + "name": "День Победы" + }, + { + "date": "2020-06-12", + "name": "День России" + }, + { + "date": "2020-08-22", + "name": "День Государственного флага" + }, + { + "date": "2020-11-04", + "name": "День народного единства" + }, + { + "date": "2021-01-01", + "name": "Новый год" + }, + { + "date": "2021-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2021-01-07", + "name": "Рождество Христово" + }, + { + "date": "2021-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2021-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2021-03-08", + "name": "Международный женский день" + }, + { + "date": "2021-05-01", + "name": "День весны и труда" + }, + { + "date": "2021-05-09", + "name": "День Победы" + }, + { + "date": "2021-06-12", + "name": "День России" + }, + { + "date": "2021-08-22", + "name": "День Государственного флага" + }, + { + "date": "2021-11-04", + "name": "День народного единства" + } + ] + }, + "RW": { + "name": "Repubulika y'u Rwanda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2018-02-01", + "name": "Heroes Day" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2018-08-03", + "name": "Umuganura" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2019-02-01", + "name": "Heroes Day" + }, + { + "date": "2019-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2019-08-02", + "name": "Umuganura" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2020-02-01", + "name": "Heroes Day" + }, + { + "date": "2020-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-07", + "name": "Umuganura" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2021-02-01", + "name": "Heroes Day" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-06", + "name": "Umuganura" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "SE": { + "name": "Sverige", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2018-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2018-03-30", + "name": "Långfredagen" + }, + { + "date": "2018-04-02", + "name": "Annandag påsk" + }, + { + "date": "2018-05-01", + "name": "Första Maj" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2018-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2018-06-23", + "name": "Midsommar" + }, + { + "date": "2018-11-03", + "name": "Alla Helgons dag" + }, + { + "date": "2018-12-25", + "name": "Juldagen" + }, + { + "date": "2018-12-26", + "name": "Annandag jul" + }, + { + "date": "2019-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2019-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2019-04-19", + "name": "Långfredagen" + }, + { + "date": "2019-04-22", + "name": "Annandag påsk" + }, + { + "date": "2019-05-01", + "name": "Första Maj" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2019-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2019-06-22", + "name": "Midsommar" + }, + { + "date": "2019-11-02", + "name": "Alla Helgons dag" + }, + { + "date": "2019-12-25", + "name": "Juldagen" + }, + { + "date": "2019-12-26", + "name": "Annandag jul" + }, + { + "date": "2020-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2020-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2020-04-10", + "name": "Långfredagen" + }, + { + "date": "2020-04-13", + "name": "Annandag påsk" + }, + { + "date": "2020-05-01", + "name": "Första Maj" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2020-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2020-06-20", + "name": "Midsommar" + }, + { + "date": "2020-10-31", + "name": "Alla Helgons dag" + }, + { + "date": "2020-12-25", + "name": "Juldagen" + }, + { + "date": "2020-12-26", + "name": "Annandag jul" + }, + { + "date": "2021-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2021-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2021-04-02", + "name": "Långfredagen" + }, + { + "date": "2021-04-05", + "name": "Annandag påsk" + }, + { + "date": "2021-05-01", + "name": "Första Maj" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2021-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2021-06-26", + "name": "Midsommar" + }, + { + "date": "2021-11-06", + "name": "Alla Helgons dag" + }, + { + "date": "2021-12-25", + "name": "Juldagen" + }, + { + "date": "2021-12-26", + "name": "Annandag jul" + } + ] + }, + "SI": { + "name": "Republika Slovenija", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "novo leto" + }, + { + "date": "2018-01-02", + "name": "novo leto" + }, + { + "date": "2018-02-08", + "name": "Prešernov dan" + }, + { + "date": "2018-04-01", + "name": "velika noč" + }, + { + "date": "2018-04-02", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2018-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2018-05-01", + "name": "praznik dela" + }, + { + "date": "2018-05-02", + "name": "praznik dela" + }, + { + "date": "2018-05-20", + "name": "binkošti" + }, + { + "date": "2018-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2018-06-25", + "name": "dan državnosti" + }, + { + "date": "2018-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2018-10-31", + "name": "dan reformacije" + }, + { + "date": "2018-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2018-12-25", + "name": "božič" + }, + { + "date": "2018-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2019-01-01", + "name": "novo leto" + }, + { + "date": "2019-01-02", + "name": "novo leto" + }, + { + "date": "2019-02-08", + "name": "Prešernov dan" + }, + { + "date": "2019-04-21", + "name": "velika noč" + }, + { + "date": "2019-04-22", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2019-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2019-05-01", + "name": "praznik dela" + }, + { + "date": "2019-05-02", + "name": "praznik dela" + }, + { + "date": "2019-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2019-06-09", + "name": "binkošti" + }, + { + "date": "2019-06-25", + "name": "dan državnosti" + }, + { + "date": "2019-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2019-10-31", + "name": "dan reformacije" + }, + { + "date": "2019-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2019-12-25", + "name": "božič" + }, + { + "date": "2019-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2020-01-01", + "name": "novo leto" + }, + { + "date": "2020-01-02", + "name": "novo leto" + }, + { + "date": "2020-02-08", + "name": "Prešernov dan" + }, + { + "date": "2020-04-12", + "name": "velika noč" + }, + { + "date": "2020-04-13", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2020-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2020-05-01", + "name": "praznik dela" + }, + { + "date": "2020-05-02", + "name": "praznik dela" + }, + { + "date": "2020-05-31", + "name": "binkošti" + }, + { + "date": "2020-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2020-06-25", + "name": "dan državnosti" + }, + { + "date": "2020-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2020-10-31", + "name": "dan reformacije" + }, + { + "date": "2020-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2020-12-25", + "name": "božič" + }, + { + "date": "2020-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2021-01-01", + "name": "novo leto" + }, + { + "date": "2021-01-02", + "name": "novo leto" + }, + { + "date": "2021-02-08", + "name": "Prešernov dan" + }, + { + "date": "2021-04-04", + "name": "velika noč" + }, + { + "date": "2021-04-05", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2021-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2021-05-01", + "name": "praznik dela" + }, + { + "date": "2021-05-02", + "name": "praznik dela" + }, + { + "date": "2021-05-23", + "name": "binkošti" + }, + { + "date": "2021-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2021-06-25", + "name": "dan državnosti" + }, + { + "date": "2021-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2021-10-31", + "name": "dan reformacije" + }, + { + "date": "2021-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2021-12-25", + "name": "božič" + }, + { + "date": "2021-12-26", + "name": "dan samostojnosti in enotnosti" + } + ] + }, + "SJ": { + "name": "Svalbard & Jan Mayen", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2018-03-29", + "name": "Skjærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Første påskedag" + }, + { + "date": "2018-04-02", + "name": "Andre påskedag" + }, + { + "date": "2018-05-01", + "name": "Første mai" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2018-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2018-05-20", + "name": "Første pinsedag" + }, + { + "date": "2018-05-21", + "name": "Andre pinsedag" + }, + { + "date": "2018-12-24", + "name": "Julaften" + }, + { + "date": "2018-12-25", + "name": "Første Juledag" + }, + { + "date": "2018-12-26", + "name": "Andre juledag" + }, + { + "date": "2018-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2019-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2019-04-18", + "name": "Skjærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Første påskedag" + }, + { + "date": "2019-04-22", + "name": "Andre påskedag" + }, + { + "date": "2019-05-01", + "name": "Første mai" + }, + { + "date": "2019-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Første pinsedag" + }, + { + "date": "2019-06-10", + "name": "Andre pinsedag" + }, + { + "date": "2019-12-24", + "name": "Julaften" + }, + { + "date": "2019-12-25", + "name": "Første Juledag" + }, + { + "date": "2019-12-26", + "name": "Andre juledag" + }, + { + "date": "2019-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2020-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2020-04-09", + "name": "Skjærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Første påskedag" + }, + { + "date": "2020-04-13", + "name": "Andre påskedag" + }, + { + "date": "2020-05-01", + "name": "Første mai" + }, + { + "date": "2020-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Første pinsedag" + }, + { + "date": "2020-06-01", + "name": "Andre pinsedag" + }, + { + "date": "2020-12-24", + "name": "Julaften" + }, + { + "date": "2020-12-25", + "name": "Første Juledag" + }, + { + "date": "2020-12-26", + "name": "Andre juledag" + }, + { + "date": "2020-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2021-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2021-04-01", + "name": "Skjærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Første påskedag" + }, + { + "date": "2021-04-05", + "name": "Andre påskedag" + }, + { + "date": "2021-05-01", + "name": "Første mai" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2021-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2021-05-23", + "name": "Første pinsedag" + }, + { + "date": "2021-05-24", + "name": "Andre pinsedag" + }, + { + "date": "2021-12-24", + "name": "Julaften" + }, + { + "date": "2021-12-25", + "name": "Første Juledag" + }, + { + "date": "2021-12-26", + "name": "Andre juledag" + }, + { + "date": "2021-12-31", + "name": "Nyttårsaften" + } + ] + }, + "SK": { + "name": "Slovenská republika", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2018-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2018-03-30", + "name": "Veľkonočný piatok" + }, + { + "date": "2018-04-02", + "name": "Veľkonočný pondelok" + }, + { + "date": "2018-05-01", + "name": "Sviatok práce" + }, + { + "date": "2018-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2018-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2018-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2018-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2018-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2018-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2018-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2018-12-24", + "name": "Štedrý deň" + }, + { + "date": "2018-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2018-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2019-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2019-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2019-04-19", + "name": "Veľkonočný piatok" + }, + { + "date": "2019-04-22", + "name": "Veľkonočný pondelok" + }, + { + "date": "2019-05-01", + "name": "Sviatok práce" + }, + { + "date": "2019-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2019-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2019-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2019-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2019-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2019-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2019-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2019-12-24", + "name": "Štedrý deň" + }, + { + "date": "2019-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2019-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2020-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2020-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2020-04-10", + "name": "Veľkonočný piatok" + }, + { + "date": "2020-04-13", + "name": "Veľkonočný pondelok" + }, + { + "date": "2020-05-01", + "name": "Sviatok práce" + }, + { + "date": "2020-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2020-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2020-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2020-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2020-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2020-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2020-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2020-12-24", + "name": "Štedrý deň" + }, + { + "date": "2020-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2020-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2021-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2021-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2021-04-02", + "name": "Veľkonočný piatok" + }, + { + "date": "2021-04-05", + "name": "Veľkonočný pondelok" + }, + { + "date": "2021-05-01", + "name": "Sviatok práce" + }, + { + "date": "2021-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2021-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2021-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2021-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2021-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2021-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2021-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2021-12-24", + "name": "Štedrý deň" + }, + { + "date": "2021-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2021-12-26", + "name": "Druhý sviatok vianočný" + } + ] + }, + "SM": { + "name": "San Marino", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Capodanno" + }, + { + "date": "2018-01-06", + "name": "Epifania" + }, + { + "date": "2018-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2018-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2018-04-01", + "name": "Domenica di Pasqua" + }, + { + "date": "2018-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2018-05-31", + "name": "Corpus Domini" + }, + { + "date": "2018-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2018-08-15", + "name": "Ferragosto" + }, + { + "date": "2018-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2018-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2018-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2018-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Capodanno" + }, + { + "date": "2019-01-06", + "name": "Epifania" + }, + { + "date": "2019-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2019-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2019-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2019-04-21", + "name": "Domenica di Pasqua" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2019-06-20", + "name": "Corpus Domini" + }, + { + "date": "2019-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2019-08-15", + "name": "Ferragosto" + }, + { + "date": "2019-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2019-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2019-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2019-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Capodanno" + }, + { + "date": "2020-01-06", + "name": "Epifania" + }, + { + "date": "2020-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2020-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2020-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2020-04-12", + "name": "Domenica di Pasqua" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2020-06-11", + "name": "Corpus Domini" + }, + { + "date": "2020-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2020-08-15", + "name": "Ferragosto" + }, + { + "date": "2020-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2020-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2020-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2020-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Capodanno" + }, + { + "date": "2021-01-06", + "name": "Epifania" + }, + { + "date": "2021-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2021-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2021-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2021-04-04", + "name": "Domenica di Pasqua" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2021-06-03", + "name": "Corpus Domini" + }, + { + "date": "2021-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2021-08-15", + "name": "Ferragosto" + }, + { + "date": "2021-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2021-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2021-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2021-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "SO": { + "name": "Jamhuuriyadda Federaalka Soomaaliya", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2018-05-01", + "name": "يوم العمال" + }, + { + "date": "2018-06-15", + "name": "عيد الفطر" + }, + { + "date": "2018-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2018-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2018-08-21", + "name": "عيد الأضحى" + }, + { + "date": "2018-09-20", + "name": "عاشوراء" + }, + { + "date": "2018-11-20", + "name": "المولد النبويّ" + }, + { + "date": "2019-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2019-05-01", + "name": "يوم العمال" + }, + { + "date": "2019-06-04", + "name": "عيد الفطر" + }, + { + "date": "2019-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2019-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2019-08-11", + "name": "عيد الأضحى" + }, + { + "date": "2019-09-09", + "name": "عاشوراء" + }, + { + "date": "2019-11-09", + "name": "المولد النبويّ" + }, + { + "date": "2020-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2020-05-01", + "name": "يوم العمال" + }, + { + "date": "2020-05-24", + "name": "عيد الفطر" + }, + { + "date": "2020-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2020-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2020-07-31", + "name": "عيد الأضحى" + }, + { + "date": "2020-08-29", + "name": "عاشوراء" + }, + { + "date": "2020-10-29", + "name": "المولد النبويّ" + }, + { + "date": "2021-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2021-05-01", + "name": "يوم العمال" + }, + { + "date": "2021-05-13", + "name": "عيد الفطر" + }, + { + "date": "2021-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2021-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2021-07-20", + "name": "عيد الأضحى" + }, + { + "date": "2021-08-18", + "name": "عاشوراء" + }, + { + "date": "2021-10-18", + "name": "المولد النبويّ" + } + ] + }, + "SS": { + "name": "South Sudan", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2018-03-08", + "name": "International Women's Day" + }, + { + "date": "2018-05-16", + "name": "SPLA Day" + }, + { + "date": "2018-06-16", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-07-09", + "name": "Independence Day" + }, + { + "date": "2018-07-30", + "name": "Martyrs Day" + }, + { + "date": "2018-08-23", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-28", + "name": "Republic Day" + }, + { + "date": "2018-12-31", + "name": "New Year's Eve" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2019-03-08", + "name": "International Women's Day" + }, + { + "date": "2019-05-16", + "name": "SPLA Day" + }, + { + "date": "2019-06-05", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-07-09", + "name": "Independence Day" + }, + { + "date": "2019-07-30", + "name": "Martyrs Day" + }, + { + "date": "2019-08-13", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-28", + "name": "Republic Day" + }, + { + "date": "2019-12-31", + "name": "New Year's Eve" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2020-03-08", + "name": "International Women's Day" + }, + { + "date": "2020-05-16", + "name": "SPLA Day" + }, + { + "date": "2020-05-25", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-07-09", + "name": "Independence Day" + }, + { + "date": "2020-07-30", + "name": "Martyrs Day" + }, + { + "date": "2020-08-02", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-28", + "name": "Republic Day" + }, + { + "date": "2020-12-31", + "name": "New Year's Eve" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2021-03-08", + "name": "International Women's Day" + }, + { + "date": "2021-05-14", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-05-16", + "name": "SPLA Day" + }, + { + "date": "2021-07-09", + "name": "Independence Day" + }, + { + "date": "2021-07-22", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-07-30", + "name": "Martyrs Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-28", + "name": "Republic Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Eve" + } + ] + }, + "SV": { + "name": "El Salvador", + "bank_holidays": [ + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-03-31", + "name": "Sabado Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-20", + "name": "Sabado Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Sabado Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-03", + "name": "Sabado Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "TG": { + "name": "République togolaise", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-13", + "name": "Jour de la libération" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-16", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-22", + "name": "Fête du mouton" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-21", + "name": "Mawlid" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-13", + "name": "Jour de la libération" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-05", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2019-08-12", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-10", + "name": "Mawlid" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-13", + "name": "Jour de la libération" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-25", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2020-08-01", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-30", + "name": "Mawlid" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-13", + "name": "Jour de la libération" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-14", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2021-07-21", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-19", + "name": "Mawlid" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "TR": { + "name": "Türkiye", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Yılbaşı" + }, + { + "date": "2018-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2018-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2018-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2018-06-15", + "name": "Bayramı" + }, + { + "date": "2018-08-21", + "name": "Kurban Bayramı" + }, + { + "date": "2018-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2018-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2019-01-01", + "name": "Yılbaşı" + }, + { + "date": "2019-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2019-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2019-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2019-06-04", + "name": "Bayramı" + }, + { + "date": "2019-08-11", + "name": "Kurban Bayramı" + }, + { + "date": "2019-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2019-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2020-01-01", + "name": "Yılbaşı" + }, + { + "date": "2020-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2020-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2020-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2020-05-24", + "name": "Bayramı" + }, + { + "date": "2020-07-31", + "name": "Kurban Bayramı" + }, + { + "date": "2020-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2020-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2021-01-01", + "name": "Yılbaşı" + }, + { + "date": "2021-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2021-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2021-05-13", + "name": "Bayramı" + }, + { + "date": "2021-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2021-07-20", + "name": "Kurban Bayramı" + }, + { + "date": "2021-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2021-10-29", + "name": "Cumhuriyet Bayramı" + } + ] + }, + "TZ": { + "name": "Tanzania", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-07", + "name": "Karume Day" + }, + { + "date": "2018-04-26", + "name": "Union Day" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-15", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2018-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2018-08-21", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-10-14", + "name": "Nyerere Day" + }, + { + "date": "2018-11-20", + "name": "Maulid Day" + }, + { + "date": "2018-12-09", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2019-04-07", + "name": "Karume Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-26", + "name": "Union Day" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-04", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2019-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2019-08-11", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-10-14", + "name": "Nyerere Day" + }, + { + "date": "2019-11-09", + "name": "Maulid Day" + }, + { + "date": "2019-12-09", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2020-04-07", + "name": "Karume Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-26", + "name": "Union Day" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2020-07-31", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2020-10-14", + "name": "Nyerere Day" + }, + { + "date": "2020-10-29", + "name": "Maulid Day" + }, + { + "date": "2020-12-09", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-07", + "name": "Karume Day" + }, + { + "date": "2021-04-26", + "name": "Union Day" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2021-07-20", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2021-10-14", + "name": "Nyerere Day" + }, + { + "date": "2021-10-18", + "name": "Maulid Day" + }, + { + "date": "2021-12-09", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Christmas Day" + } + ] + }, + "UA": { + "name": "Україна", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новий Рік" + }, + { + "date": "2018-01-02", + "name": "Новий Рік" + }, + { + "date": "2018-01-07", + "name": "Різдво" + }, + { + "date": "2018-01-08", + "name": "Різдво (замінити день)" + }, + { + "date": "2018-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2018-04-08", + "name": "Великдень" + }, + { + "date": "2018-04-09", + "name": "Великдень" + }, + { + "date": "2018-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2018-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2018-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2018-05-27", + "name": "Трійця" + }, + { + "date": "2018-05-28", + "name": "Трійця" + }, + { + "date": "2018-06-28", + "name": "День Конституції" + }, + { + "date": "2018-08-24", + "name": "День Незалежності" + }, + { + "date": "2018-10-14", + "name": "День захисника України" + }, + { + "date": "2018-10-15", + "name": "День захисника України (замінити день)" + }, + { + "date": "2019-01-01", + "name": "Новий Рік" + }, + { + "date": "2019-01-02", + "name": "Новий Рік" + }, + { + "date": "2019-01-07", + "name": "Різдво" + }, + { + "date": "2019-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2019-04-28", + "name": "Великдень" + }, + { + "date": "2019-04-29", + "name": "Великдень" + }, + { + "date": "2019-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2019-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2019-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2019-06-16", + "name": "Трійця" + }, + { + "date": "2019-06-17", + "name": "Трійця" + }, + { + "date": "2019-06-28", + "name": "День Конституції" + }, + { + "date": "2019-08-24", + "name": "День Незалежності" + }, + { + "date": "2019-08-26", + "name": "День Незалежності (замінити день)" + }, + { + "date": "2019-10-14", + "name": "День захисника України" + }, + { + "date": "2020-01-01", + "name": "Новий Рік" + }, + { + "date": "2020-01-02", + "name": "Новий Рік" + }, + { + "date": "2020-01-07", + "name": "Різдво" + }, + { + "date": "2020-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2020-03-09", + "name": "Міжнародний жіночий день" + }, + { + "date": "2020-04-19", + "name": "Великдень" + }, + { + "date": "2020-04-20", + "name": "Великдень" + }, + { + "date": "2020-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2020-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2020-05-04", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2020-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2020-05-11", + "name": "День перемоги над нацизмом у Другій світовій війні (замінити день)" + }, + { + "date": "2020-06-07", + "name": "Трійця" + }, + { + "date": "2020-06-08", + "name": "Трійця" + }, + { + "date": "2020-06-28", + "name": "День Конституції" + }, + { + "date": "2020-06-29", + "name": "День Конституції (замінити день)" + }, + { + "date": "2020-08-24", + "name": "День Незалежності" + }, + { + "date": "2020-10-14", + "name": "День захисника України" + }, + { + "date": "2021-01-01", + "name": "Новий Рік" + }, + { + "date": "2021-01-02", + "name": "Новий Рік" + }, + { + "date": "2021-01-04", + "name": "Новий Рік (замінити день)" + }, + { + "date": "2021-01-07", + "name": "Різдво" + }, + { + "date": "2021-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2021-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2021-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2021-05-02", + "name": "Великдень" + }, + { + "date": "2021-05-03", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2021-05-03", + "name": "Великдень" + }, + { + "date": "2021-05-04", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2021-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2021-05-10", + "name": "День перемоги над нацизмом у Другій світовій війні (замінити день)" + }, + { + "date": "2021-06-20", + "name": "Трійця" + }, + { + "date": "2021-06-21", + "name": "Трійця" + }, + { + "date": "2021-06-28", + "name": "День Конституції" + }, + { + "date": "2021-08-24", + "name": "День Незалежності" + }, + { + "date": "2021-10-14", + "name": "День захисника України" + } + ] + }, + "UG": { + "name": "Uganda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Liberation Day" + }, + { + "date": "2018-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2018-03-08", + "name": "International Women's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-03", + "name": "Martyr's Day" + }, + { + "date": "2018-06-09", + "name": "National Heroes Day" + }, + { + "date": "2018-06-15", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-08-21", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-10-09", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Liberation Day" + }, + { + "date": "2019-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2019-03-08", + "name": "International Women's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-03", + "name": "Martyr's Day" + }, + { + "date": "2019-06-04", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-06-09", + "name": "National Heroes Day" + }, + { + "date": "2019-08-11", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-10-09", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-26", + "name": "Liberation Day" + }, + { + "date": "2020-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2020-03-08", + "name": "International Women's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-06-03", + "name": "Martyr's Day" + }, + { + "date": "2020-06-09", + "name": "National Heroes Day" + }, + { + "date": "2020-07-31", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-10-09", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Liberation Day" + }, + { + "date": "2021-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2021-03-08", + "name": "International Women's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-06-03", + "name": "Martyr's Day" + }, + { + "date": "2021-06-09", + "name": "National Heroes Day" + }, + { + "date": "2021-07-20", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-10-09", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "US": { + "name": "United States of America", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-09-07", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + } + ] + }, + "UY": { + "name": "Uruguay", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2018-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2019-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2020-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2021-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "VA": { + "name": "Stato della Città del Vaticano", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2018-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2018-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2018-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2018-03-19", + "name": "San Giuseppe" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2018-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2018-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2018-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2018-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2018-11-01", + "name": "Ognissanti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2019-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2019-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2019-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2019-03-19", + "name": "San Giuseppe" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2019-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2019-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2019-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2019-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2019-11-01", + "name": "Ognissanti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2020-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2020-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2020-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2020-03-19", + "name": "San Giuseppe" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2020-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2020-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2020-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2020-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2020-11-01", + "name": "Ognissanti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2021-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2021-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2021-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2021-03-19", + "name": "San Giuseppe" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2021-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2021-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2021-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2021-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2021-11-01", + "name": "Ognissanti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "VE": { + "name": "Venezuela", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2018-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2018-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2018-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2019-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2019-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2019-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2020-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2020-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2020-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2021-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2021-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2021-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "VN": { + "name": "Cộng hòa Xã hội chủ nghĩa Việt Nam", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2018-02-15", + "name": "Tết ngày lễ" + }, + { + "date": "2018-02-16", + "name": "Tết" + }, + { + "date": "2018-04-25", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2018-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2018-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2018-09-02", + "name": "Quốc khánh" + }, + { + "date": "2019-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2019-02-04", + "name": "Tết ngày lễ" + }, + { + "date": "2019-02-05", + "name": "Tết" + }, + { + "date": "2019-04-14", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2019-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2019-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2019-09-02", + "name": "Quốc khánh" + }, + { + "date": "2020-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2020-01-24", + "name": "Tết ngày lễ" + }, + { + "date": "2020-01-25", + "name": "Tết" + }, + { + "date": "2020-04-02", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2020-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2020-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2020-09-02", + "name": "Quốc khánh" + }, + { + "date": "2021-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2021-02-11", + "name": "Tết ngày lễ" + }, + { + "date": "2021-02-12", + "name": "Tết" + }, + { + "date": "2021-04-21", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2021-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2021-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2021-09-02", + "name": "Quốc khánh" + } + ] + }, + "XK": { + "name": "Republika e Kosovës", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Viti i Ri" + }, + { + "date": "2018-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2018-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2018-04-01", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-08", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2018-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2018-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2018-06-15", + "name": "Fitër Bajrami" + }, + { + "date": "2018-08-21", + "name": "Kurban Bajrami" + }, + { + "date": "2018-12-25", + "name": "Krishtlindja" + }, + { + "date": "2019-01-01", + "name": "Viti i Ri" + }, + { + "date": "2019-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2019-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2019-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2019-04-21", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-28", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2019-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2019-06-04", + "name": "Fitër Bajrami" + }, + { + "date": "2019-08-11", + "name": "Kurban Bajrami" + }, + { + "date": "2019-12-25", + "name": "Krishtlindja" + }, + { + "date": "2020-01-01", + "name": "Viti i Ri" + }, + { + "date": "2020-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2020-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2020-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2020-04-12", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-19", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2020-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2020-05-24", + "name": "Fitër Bajrami" + }, + { + "date": "2020-07-31", + "name": "Kurban Bajrami" + }, + { + "date": "2020-12-25", + "name": "Krishtlindja" + }, + { + "date": "2021-01-01", + "name": "Viti i Ri" + }, + { + "date": "2021-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2021-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2021-04-04", + "name": "Pashkët Katolike" + }, + { + "date": "2021-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2021-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2021-05-02", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2021-05-13", + "name": "Fitër Bajrami" + }, + { + "date": "2021-07-20", + "name": "Kurban Bajrami" + }, + { + "date": "2021-12-25", + "name": "Krishtlindja" + } + ] + }, + "YT": { + "name": "Mayotte", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "ZA": { + "name": "South Africa", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-21", + "name": "Human Rights Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Family Day" + }, + { + "date": "2018-04-27", + "name": "Freedom Day" + }, + { + "date": "2018-05-01", + "name": "Workers' Day" + }, + { + "date": "2018-06-16", + "name": "Youth Day" + }, + { + "date": "2018-08-09", + "name": "National Women's Day" + }, + { + "date": "2018-09-24", + "name": "Heritage Day" + }, + { + "date": "2018-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2018-12-17", + "name": "Public Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-21", + "name": "Human Rights Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Family Day" + }, + { + "date": "2019-04-27", + "name": "Freedom Day" + }, + { + "date": "2019-05-01", + "name": "Workers' Day" + }, + { + "date": "2019-06-16", + "name": "Youth Day" + }, + { + "date": "2019-06-17", + "name": "Public Holiday" + }, + { + "date": "2019-08-09", + "name": "National Women's Day" + }, + { + "date": "2019-09-24", + "name": "Heritage Day" + }, + { + "date": "2019-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-21", + "name": "Human Rights Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Family Day" + }, + { + "date": "2020-04-27", + "name": "Freedom Day" + }, + { + "date": "2020-05-01", + "name": "Workers' Day" + }, + { + "date": "2020-06-16", + "name": "Youth Day" + }, + { + "date": "2020-08-09", + "name": "National Women's Day" + }, + { + "date": "2020-08-10", + "name": "Public Holiday" + }, + { + "date": "2020-09-24", + "name": "Heritage Day" + }, + { + "date": "2020-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-21", + "name": "Human Rights Day" + }, + { + "date": "2021-03-22", + "name": "Public Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Family Day" + }, + { + "date": "2021-04-27", + "name": "Freedom Day" + }, + { + "date": "2021-05-01", + "name": "Workers' Day" + }, + { + "date": "2021-06-16", + "name": "Youth Day" + }, + { + "date": "2021-08-09", + "name": "National Women's Day" + }, + { + "date": "2021-09-24", + "name": "Heritage Day" + }, + { + "date": "2021-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" + } + ] + }, + "ZM": { + "name": "Zambia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-09", + "name": "Women’s Day" + }, + { + "date": "2018-03-12", + "name": "Youth Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-25", + "name": "African Freedom Day" + }, + { + "date": "2018-07-02", + "name": "Heroes' Day" + }, + { + "date": "2018-07-03", + "name": "Unity Day" + }, + { + "date": "2018-08-06", + "name": "Farmers' Day" + }, + { + "date": "2018-10-24", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-09", + "name": "Women’s Day" + }, + { + "date": "2019-03-12", + "name": "Youth Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-25", + "name": "African Freedom Day" + }, + { + "date": "2019-07-01", + "name": "Heroes' Day" + }, + { + "date": "2019-07-02", + "name": "Unity Day" + }, + { + "date": "2019-08-05", + "name": "Farmers' Day" + }, + { + "date": "2019-10-24", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Women’s Day" + }, + { + "date": "2020-03-12", + "name": "Youth Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-25", + "name": "African Freedom Day" + }, + { + "date": "2020-07-06", + "name": "Heroes' Day" + }, + { + "date": "2020-07-07", + "name": "Unity Day" + }, + { + "date": "2020-08-03", + "name": "Farmers' Day" + }, + { + "date": "2020-10-24", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-09", + "name": "Women’s Day" + }, + { + "date": "2021-03-12", + "name": "Youth Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-25", + "name": "African Freedom Day" + }, + { + "date": "2021-07-05", + "name": "Heroes' Day" + }, + { + "date": "2021-07-06", + "name": "Unity Day" + }, + { + "date": "2021-08-02", + "name": "Farmers' Day" + }, + { + "date": "2021-10-24", + "name": "Independence Day" + }, + { + "date": "2021-10-25", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + } + ] + }, + "ZW": { + "name": "Zimbabwe", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-18", + "name": "Independence Day" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-25", + "name": "Africa Day" + }, + { + "date": "2018-08-13", + "name": "Heroes' Day" + }, + { + "date": "2018-08-14", + "name": "Defence Forces Day" + }, + { + "date": "2018-12-22", + "name": "Unity Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-18", + "name": "Independence Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-25", + "name": "Africa Day" + }, + { + "date": "2019-08-12", + "name": "Heroes' Day" + }, + { + "date": "2019-08-13", + "name": "Defence Forces Day" + }, + { + "date": "2019-12-22", + "name": "Unity Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-18", + "name": "Independence Day" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-25", + "name": "Africa Day" + }, + { + "date": "2020-08-10", + "name": "Heroes' Day" + }, + { + "date": "2020-08-11", + "name": "Defence Forces Day" + }, + { + "date": "2020-12-22", + "name": "Unity Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-18", + "name": "Independence Day" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-25", + "name": "Africa Day" + }, + { + "date": "2021-08-09", + "name": "Heroes' Day" + }, + { + "date": "2021-08-10", + "name": "Defence Forces Day" + }, + { + "date": "2021-12-22", + "name": "Unity Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "ZZ": { + "name": "Other", + "bank_holidays": [ + { + "name": "Early May bank holiday", + "date": "2015-05-04" + } + ] + } + } +} diff --git a/config.example/sequelize-config.js b/config.example/sequelize-config.js new file mode 100644 index 000000000..0491dde3b --- /dev/null +++ b/config.example/sequelize-config.js @@ -0,0 +1,15 @@ +require('dotenv').config() + +module.exports = { + development: { + url: process.env.DATABASE_URL, + dialect: process.env.DB_DIALECT || 'postgres' + // Additional config settings as needed + }, + production: { + url: process.env.DATABASE_URL, + dialect: process.env.DB_DIALECT || 'postgres' + // Additional production-specific settings + } + // Additional environment configurations as needed +} diff --git a/config.example/timeoff-dev.code-workspace b/config.example/timeoff-dev.code-workspace new file mode 100644 index 000000000..2a0ed79b2 --- /dev/null +++ b/config.example/timeoff-dev.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": ".." + } + ] +} \ No newline at end of file diff --git a/config/app.json b/config/app.json index 689890f2d..b36276a03 100644 --- a/config/app.json +++ b/config/app.json @@ -1,16 +1,49 @@ { - "allow_create_new_accounts" : true, - "send_emails" : false, - "application_sender_email" : "email@test.com", - "email_transporter" : { - "host" : "localhost", - "port" : 25, - "auth" : { - "user" : "user", - "pass" : "pass" + "branding": { + "url": "http://config.branding.url", + "website": "http://config.branding.website" + }, + "crypto_secret": "!2~`HswpPPLa22+=±§sdq qwe,appp qwwokDF_", + "login": { + "default": true, + "google": false + }, + "smtp": { + "host": "localhost", + "port": 25, + "from": "email@test.com", + "auth": { + "user": "", + "pass": "", + "required": true + } + }, + "sessions": { + "secret": "my dirty secret ;khjsdkjahsdajhasdam,nnsnad,", + "store": "sequelize", + "redis": { + "host": "localhost", + "port": 6379 } }, - "crypto_secret" : "!2~`HswpPPLa22+=±§sdq qwe,appp qwwokDF_", - "application_domain" : "http://app.timeoff.management", - "promotion_website_domain" : "http://timeoff.management" + "google": { + "analytics": { + "tracker": "" + }, + "auth": { + "clientId": "123", + "clientSecret": "123", + "domains": ["mydomain.com"] + } + }, + "slack": { + "token": "Get your Web API token from you Slack admin page.", + "icon_url": "The image can be hosted anywhere, but I would recoment to upload an icon to your Slack and use it's url.", + "bot_name": "The display name for the messages being sent." + }, + "options": { + "registration": true + }, + "locale_code_for_sorting": "en", + "force_to_explicitly_select_type_when_requesting_new_leave": false } diff --git a/config/localisation.json b/config/localisation.json index 30087f025..d44b720c8 100644 --- a/config/localisation.json +++ b/config/localisation.json @@ -1,268 +1,27285 @@ { - "countries" : { - "CA" : { - "name" : "Canada", - "bank_holidays" : [ + "countries": { + "AD": { + "name": "Andorra", + "bank_holidays": [ { - "name" : "New Year Day", - "date" : "2017-01-01" + "date": "2018-01-01", + "name": "Any nou" }, { - "name" : "Victoria Day", - "date" : "2017-05-22" + "date": "2018-01-06", + "name": "Día de los Reyes Magos" }, { - "name" : "Canada Day", - "date" : "2017-07-01" + "date": "2018-02-13", + "name": "Carnaval" }, { - "name" : "Labour Day", - "date" : "2017-09-04" + "date": "2018-03-14", + "name": "Dia de la Constitució" }, { - "name" : "Christmas Day", - "date" : "2017-12-25" + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-04-02", + "name": "Lunes de Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-20", + "name": "Pentecostés" + }, + { + "date": "2018-05-21", + "name": "Lunes de Pentecostés" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Nadal" + }, + { + "date": "2018-12-26", + "name": "San Esteban" + }, + { + "date": "2019-01-01", + "name": "Any nou" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-04-22", + "name": "Lunes de Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-09", + "name": "Pentecostés" + }, + { + "date": "2019-06-10", + "name": "Lunes de Pentecostés" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Nadal" + }, + { + "date": "2019-12-26", + "name": "San Esteban" + }, + { + "date": "2020-01-01", + "name": "Any nou" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-04-13", + "name": "Lunes de Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-31", + "name": "Pentecostés" + }, + { + "date": "2020-06-01", + "name": "Lunes de Pentecostés" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Nadal" + }, + { + "date": "2020-12-26", + "name": "San Esteban" + }, + { + "date": "2021-01-01", + "name": "Any nou" + }, + { + "date": "2021-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-14", + "name": "Dia de la Constitució" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-04-05", + "name": "Lunes de Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-23", + "name": "Pentecostés" + }, + { + "date": "2021-05-24", + "name": "Lunes de Pentecostés" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-08", + "name": "Mare de Déu de Meritxell" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Nadal" + }, + { + "date": "2021-12-26", + "name": "San Esteban" + } + ] + }, + "AG": { + "name": "Antigua & Barbuda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2018-08-02", + "name": "Last Lap" + }, + { + "date": "2018-11-01", + "name": "Independence Day" + }, + { + "date": "2018-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2019-08-02", + "name": "Last Lap" + }, + { + "date": "2019-11-01", + "name": "Independence Day" + }, + { + "date": "2019-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2020-08-02", + "name": "Last Lap" + }, + { + "date": "2020-11-02", + "name": "Independence Day" + }, + { + "date": "2020-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-01", + "name": "J'Ouvert Morning" + }, + { + "date": "2021-08-02", + "name": "Last Lap" + }, + { + "date": "2021-11-01", + "name": "Independence Day" + }, + { + "date": "2021-12-09", + "name": "V.C Bird Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AI": { + "name": "Anguilla", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-05-30", + "name": "Anguilla Day" + }, + { + "date": "2018-06-11", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2018-08-02", + "name": "August Thursday" + }, + { + "date": "2018-08-03", + "name": "Constitution Day" + }, + { + "date": "2018-08-06", + "name": "August Monday" + }, + { + "date": "2018-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-30", + "name": "Anguilla Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-06-10", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2019-08-01", + "name": "August Thursday" + }, + { + "date": "2019-08-02", + "name": "Constitution Day" + }, + { + "date": "2019-08-05", + "name": "August Monday" + }, + { + "date": "2019-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-30", + "name": "Anguilla Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-08", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2020-08-03", + "name": "August Monday" + }, + { + "date": "2020-08-06", + "name": "August Thursday" + }, + { + "date": "2020-08-07", + "name": "Constitution Day" + }, + { + "date": "2020-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-02", + "name": "James Ronald Webster Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-05-30", + "name": "Anguilla Day" + }, + { + "date": "2021-06-14", + "name": "Celebration of the Birthday of Her Majesty the Queen" + }, + { + "date": "2021-08-02", + "name": "August Monday" + }, + { + "date": "2021-08-05", + "name": "August Thursday" + }, + { + "date": "2021-08-06", + "name": "Constitution Day" + }, + { + "date": "2021-12-19", + "name": "National Heroes and Heroines Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AL": { + "name": "Shqipëri", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Viti i Ri" + }, + { + "date": "2018-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2018-03-14", + "name": "Dita e Verës" + }, + { + "date": "2018-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2018-04-01", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-02", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-08", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-04-09", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2018-06-15", + "name": "Fitër Bajrami" + }, + { + "date": "2018-08-21", + "name": "Kurban Bajrami" + }, + { + "date": "2018-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2018-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2018-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2018-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2018-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2018-12-25", + "name": "Krishtlindja" + }, + { + "date": "2019-01-01", + "name": "Viti i Ri" + }, + { + "date": "2019-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2019-03-14", + "name": "Dita e Verës" + }, + { + "date": "2019-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2019-04-21", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-22", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-28", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-04-29", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2019-06-04", + "name": "Fitër Bajrami" + }, + { + "date": "2019-08-11", + "name": "Kurban Bajrami" + }, + { + "date": "2019-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2019-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2019-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2019-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2019-12-09", + "name": "Dita Kombëtare e Rinisë (ditë zëvendësuese)" + }, + { + "date": "2019-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2019-12-25", + "name": "Krishtlindja" + }, + { + "date": "2020-01-01", + "name": "Viti i Ri" + }, + { + "date": "2020-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2020-03-14", + "name": "Dita e Verës" + }, + { + "date": "2020-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2020-03-23", + "name": "Dita e Sulltan Nevruzit (ditë zëvendësuese)" + }, + { + "date": "2020-04-12", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-13", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-19", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-04-20", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2020-05-24", + "name": "Fitër Bajrami" + }, + { + "date": "2020-07-31", + "name": "Kurban Bajrami" + }, + { + "date": "2020-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2020-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2020-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2020-11-30", + "name": "Dita e Çlirimit (ditë zëvendësuese)" + }, + { + "date": "2020-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2020-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2020-12-25", + "name": "Krishtlindja" + }, + { + "date": "2021-01-01", + "name": "Viti i Ri" + }, + { + "date": "2021-01-02", + "name": "Festa e Vitit të Ri" + }, + { + "date": "2021-03-14", + "name": "Dita e Verës" + }, + { + "date": "2021-03-15", + "name": "Dita e Verës (ditë zëvendësuese)" + }, + { + "date": "2021-03-22", + "name": "Dita e Sulltan Nevruzit" + }, + { + "date": "2021-04-04", + "name": "Pashkët Katolike" + }, + { + "date": "2021-04-05", + "name": "Pashkët Katolike" + }, + { + "date": "2021-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2021-05-02", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-03", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-13", + "name": "Fitër Bajrami" + }, + { + "date": "2021-07-20", + "name": "Kurban Bajrami" + }, + { + "date": "2021-10-19", + "name": "Dita e Nënë Terezës" + }, + { + "date": "2021-11-28", + "name": "Dita e Pavarësisë" + }, + { + "date": "2021-11-29", + "name": "Dita e Pavarësisë (ditë zëvendësuese)" + }, + { + "date": "2021-11-29", + "name": "Dita e Çlirimit" + }, + { + "date": "2021-12-08", + "name": "Dita Kombëtare e Rinisë" + }, + { + "date": "2021-12-24", + "name": "Nata e Krishtlindjes" + }, + { + "date": "2021-12-25", + "name": "Krishtlindja" + } + ] + }, + "AM": { + "name": "Հայաստան", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ամանոր" + }, + { + "date": "2018-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2018-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2018-01-28", + "name": "Բանակի օր" + }, + { + "date": "2018-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2018-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2018-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2018-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2018-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2018-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2018-09-21", + "name": "Անկախության օր" + }, + { + "date": "2018-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2019-01-01", + "name": "Ամանոր" + }, + { + "date": "2019-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2019-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2019-01-28", + "name": "Բանակի օր" + }, + { + "date": "2019-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2019-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2019-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2019-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2019-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2019-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2019-09-21", + "name": "Անկախության օր" + }, + { + "date": "2019-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2020-01-01", + "name": "Ամանոր" + }, + { + "date": "2020-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2020-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2020-01-28", + "name": "Բանակի օր" + }, + { + "date": "2020-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2020-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2020-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2020-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2020-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2020-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2020-09-21", + "name": "Անկախության օր" + }, + { + "date": "2020-12-31", + "name": "Նոր տարվա գիշեր" + }, + { + "date": "2021-01-01", + "name": "Ամանոր" + }, + { + "date": "2021-01-03", + "name": "Նախածննդյան տոներ" + }, + { + "date": "2021-01-06", + "name": "Սուրբ Ծնունդ" + }, + { + "date": "2021-01-28", + "name": "Բանակի օր" + }, + { + "date": "2021-03-08", + "name": "Կանանց տոն" + }, + { + "date": "2021-04-24", + "name": "Ցեղասպանության զոհերի հիշատակի օր" + }, + { + "date": "2021-05-01", + "name": "Աշխատանքի օր" + }, + { + "date": "2021-05-09", + "name": "Հաղթանակի եւ Խաղաղության տոն" + }, + { + "date": "2021-05-28", + "name": "Հանրապետության օր" + }, + { + "date": "2021-07-05", + "name": "Սահմանադրության օր" + }, + { + "date": "2021-09-21", + "name": "Անկախության օր" + }, + { + "date": "2021-12-31", + "name": "Նոր տարվա գիշեր" + } + ] + }, + "AO": { + "name": "Angola", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-04", + "name": "Dia da Paz" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2018-11-02", + "name": "Dia de Finados" + }, + { + "date": "2018-11-11", + "name": "Dia da Independência" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2019-04-04", + "name": "Dia da Paz" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2019-11-02", + "name": "Dia de Finados" + }, + { + "date": "2019-11-11", + "name": "Dia da Independência" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2020-04-04", + "name": "Dia da Paz" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2020-11-02", + "name": "Dia de Finados" + }, + { + "date": "2020-11-11", + "name": "Dia da Independência" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-02-04", + "name": "Dia do Início da Luta Armada de Libertação Nacional" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-08", + "name": "Dia Internacional da Mulher" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-04", + "name": "Dia da Paz" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-09-17", + "name": "Fundador da Nação e Dia dos Heróis Nacionais" + }, + { + "date": "2021-11-02", + "name": "Dia de Finados" + }, + { + "date": "2021-11-11", + "name": "Dia da Independência" + }, + { + "date": "2021-12-25", + "name": "Natal" + } + ] + }, + "AR": { + "name": "Argentina", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2018-04-30", + "name": "Feriado Puente Turístico" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2018-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2018-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-20", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2018-10-15", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2018-11-26", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2019-04-01", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2019-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2019-06-21", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-19", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2019-10-14", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2019-11-25", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-23", + "name": "Feriado Puente Turístico" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-03-23", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2020-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2020-04-03", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2020-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2020-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-17", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2020-10-12", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2020-11-23", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2020-12-07", + "name": "Feriado Puente Turístico" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-03-24", + "name": "Día de la Memoria por la Verdad y la Justicia" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-02", + "name": "Día del Veterano y de los Caídos en la Guerra de Malvinas" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-24", + "name": "Feriado Puente Turístico" + }, + { + "date": "2021-05-25", + "name": "Primer Gobierno Patrio" + }, + { + "date": "2021-06-20", + "name": "Día de la Bandera" + }, + { + "date": "2021-07-09", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-16", + "name": "Día del Libertador José de San Martín" + }, + { + "date": "2021-10-11", + "name": "Día del Respeto a la Diversidad Cultural" + }, + { + "date": "2021-11-22", + "name": "Día de la Soberanía nacional" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "AS": { + "name": "American Samoa", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-09-03", + "name": "Labour Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-24", + "name": "Christmas Eve" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-31", + "name": "New Year's Eve" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-09-02", + "name": "Labour Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-24", + "name": "Christmas Eve" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-31", + "name": "New Year's Eve" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-09-07", + "name": "Labour Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-24", + "name": "Christmas Eve" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-31", + "name": "New Year's Eve" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-04-17", + "name": "American Samoa Flag Day" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-09-06", + "name": "Labour Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-24", + "name": "Christmas Eve" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + }, + { + "date": "2021-12-31", + "name": "New Year's Eve" + } + ] + }, + "AT": { + "name": "Österreich", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2018-04-01", + "name": "Ostersonntag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2018-05-10", + "name": "Christi Himmelfahrt" + }, + { + "date": "2018-05-20", + "name": "Pfingstsonntag" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-05-31", + "name": "Fronleichnam" + }, + { + "date": "2018-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2018-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2018-11-01", + "name": "Allerheiligen" + }, + { + "date": "2018-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "Christtag" + }, + { + "date": "2018-12-26", + "name": "Stefanitag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2019-04-21", + "name": "Ostersonntag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2019-05-30", + "name": "Christi Himmelfahrt" + }, + { + "date": "2019-06-09", + "name": "Pfingstsonntag" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-06-20", + "name": "Fronleichnam" + }, + { + "date": "2019-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2019-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2019-11-01", + "name": "Allerheiligen" + }, + { + "date": "2019-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "Christtag" + }, + { + "date": "2019-12-26", + "name": "Stefanitag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2020-04-12", + "name": "Ostersonntag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2020-05-21", + "name": "Christi Himmelfahrt" + }, + { + "date": "2020-05-31", + "name": "Pfingstsonntag" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-06-11", + "name": "Fronleichnam" + }, + { + "date": "2020-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2020-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2020-11-01", + "name": "Allerheiligen" + }, + { + "date": "2020-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "Christtag" + }, + { + "date": "2020-12-26", + "name": "Stefanitag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2021-04-04", + "name": "Ostersonntag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Staatsfeiertag" + }, + { + "date": "2021-05-13", + "name": "Christi Himmelfahrt" + }, + { + "date": "2021-05-23", + "name": "Pfingstsonntag" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-06-03", + "name": "Fronleichnam" + }, + { + "date": "2021-08-15", + "name": "Mariä Himmelfahrt" + }, + { + "date": "2021-10-26", + "name": "Nationalfeiertag" + }, + { + "date": "2021-11-01", + "name": "Allerheiligen" + }, + { + "date": "2021-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "Christtag" + }, + { + "date": "2021-12-26", + "name": "Stefanitag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "AU": { + "name": "Australia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Australia Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-25", + "name": "ANZAC Day" + }, + { + "date": "2018-06-11", + "name": "Queen's Birthday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Australia Day" + }, + { + "date": "2019-01-28", + "name": "Australia Day (substitute day)" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "ANZAC Day" + }, + { + "date": "2019-06-10", + "name": "Queen's Birthday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-26", + "name": "Australia Day" + }, + { + "date": "2020-01-27", + "name": "Australia Day (substitute day)" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-25", + "name": "ANZAC Day" + }, + { + "date": "2020-04-27", + "name": "ANZAC Day (substitute day)" + }, + { + "date": "2020-06-08", + "name": "Queen's Birthday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Australia Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-25", + "name": "ANZAC Day" + }, + { + "date": "2021-04-26", + "name": "ANZAC Day (substitute day)" + }, + { + "date": "2021-06-14", + "name": "Queen's Birthday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "AW": { + "name": "Aruba", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Aña Nobo" + }, + { + "date": "2018-01-25", + "name": "Dia di Betico" + }, + { + "date": "2018-02-12", + "name": "Dialuna di Carnaval" + }, + { + "date": "2018-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2018-03-19", + "name": "Dia di Himno y Bandera (substituut)" + }, + { + "date": "2018-03-30", + "name": "Diabierna Santo" + }, + { + "date": "2018-04-02", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2018-04-27", + "name": "Aña di Rey" + }, + { + "date": "2018-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2018-05-10", + "name": "Dia di Asuncion" + }, + { + "date": "2018-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2018-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2019-01-01", + "name": "Aña Nobo" + }, + { + "date": "2019-01-25", + "name": "Dia di Betico" + }, + { + "date": "2019-03-04", + "name": "Dialuna di Carnaval" + }, + { + "date": "2019-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2019-04-19", + "name": "Diabierna Santo" + }, + { + "date": "2019-04-22", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2019-04-27", + "name": "Aña di Rey" + }, + { + "date": "2019-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2019-05-30", + "name": "Dia di Asuncion" + }, + { + "date": "2019-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2019-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2020-01-01", + "name": "Aña Nobo" + }, + { + "date": "2020-01-25", + "name": "Dia di Betico" + }, + { + "date": "2020-02-24", + "name": "Dialuna di Carnaval" + }, + { + "date": "2020-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2020-04-10", + "name": "Diabierna Santo" + }, + { + "date": "2020-04-13", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2020-04-27", + "name": "Aña di Rey" + }, + { + "date": "2020-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2020-05-21", + "name": "Dia di Asuncion" + }, + { + "date": "2020-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2020-12-26", + "name": "Di dos Dia Pasco di Nascimento" + }, + { + "date": "2021-01-01", + "name": "Aña Nobo" + }, + { + "date": "2021-01-25", + "name": "Dia di Betico" + }, + { + "date": "2021-02-15", + "name": "Dialuna di Carnaval" + }, + { + "date": "2021-03-18", + "name": "Dia di Himno y Bandera" + }, + { + "date": "2021-04-02", + "name": "Diabierna Santo" + }, + { + "date": "2021-04-05", + "name": "Di dos Dia Pasco di Resureccion" + }, + { + "date": "2021-04-27", + "name": "Aña di Rey" + }, + { + "date": "2021-05-01", + "name": "Dia di Obrero" + }, + { + "date": "2021-05-13", + "name": "Dia di Asuncion" + }, + { + "date": "2021-12-25", + "name": "Dia Pasco di Nascimento" + }, + { + "date": "2021-12-26", + "name": "Di dos Dia Pasco di Nascimento" + } + ] + }, + "AX": { + "name": "Landskapet Åland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2018-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2018-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2018-03-30", + "name": "Långfredagen" + }, + { + "date": "2018-04-02", + "name": "Annandag påsk" + }, + { + "date": "2018-05-01", + "name": "Första Maj" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2018-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2018-06-15", + "name": "Midsommarafton" + }, + { + "date": "2018-06-16", + "name": "Midsommardagen" + }, + { + "date": "2018-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2018-12-24", + "name": "Julafton" + }, + { + "date": "2018-12-25", + "name": "Juldagen" + }, + { + "date": "2018-12-26", + "name": "Annandag jul" + }, + { + "date": "2018-12-31", + "name": "Nyårsafton" + }, + { + "date": "2019-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2019-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2019-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2019-04-19", + "name": "Långfredagen" + }, + { + "date": "2019-04-22", + "name": "Annandag påsk" + }, + { + "date": "2019-05-01", + "name": "Första Maj" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2019-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2019-06-15", + "name": "Midsommardagen" + }, + { + "date": "2019-06-21", + "name": "Midsommarafton" + }, + { + "date": "2019-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2019-12-24", + "name": "Julafton" + }, + { + "date": "2019-12-25", + "name": "Juldagen" + }, + { + "date": "2019-12-26", + "name": "Annandag jul" + }, + { + "date": "2019-12-31", + "name": "Nyårsafton" + }, + { + "date": "2020-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2020-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2020-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2020-04-10", + "name": "Långfredagen" + }, + { + "date": "2020-04-13", + "name": "Annandag påsk" + }, + { + "date": "2020-05-01", + "name": "Första Maj" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2020-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2020-06-19", + "name": "Midsommarafton" + }, + { + "date": "2020-06-20", + "name": "Midsommardagen" + }, + { + "date": "2020-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2020-12-24", + "name": "Julafton" + }, + { + "date": "2020-12-25", + "name": "Juldagen" + }, + { + "date": "2020-12-26", + "name": "Annandag jul" + }, + { + "date": "2020-12-31", + "name": "Nyårsafton" + }, + { + "date": "2021-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2021-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2021-03-30", + "name": "Ålands demilitariseringsdag" + }, + { + "date": "2021-04-02", + "name": "Långfredagen" + }, + { + "date": "2021-04-05", + "name": "Annandag påsk" + }, + { + "date": "2021-05-01", + "name": "Första Maj" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2021-06-09", + "name": "Självstyrelsedagen" + }, + { + "date": "2021-06-18", + "name": "Midsommarafton" + }, + { + "date": "2021-06-19", + "name": "Midsommardagen" + }, + { + "date": "2021-12-06", + "name": "Självständighetsdagen" + }, + { + "date": "2021-12-24", + "name": "Julafton" + }, + { + "date": "2021-12-25", + "name": "Juldagen" + }, + { + "date": "2021-12-26", + "name": "Annandag jul" + }, + { + "date": "2021-12-31", + "name": "Nyårsafton" + } + ] + }, + "AZ": { + "name": "Azərbaycan Respublikası", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Yeni il" + }, + { + "date": "2018-01-01", + "name": "Dünya azərbaycanlıların həmrəyliyi günü (əvəz gün)" + }, + { + "date": "2018-01-02", + "name": "Yeni il" + }, + { + "date": "2018-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2018-03-20", + "name": "Novruz" + }, + { + "date": "2018-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2018-05-28", + "name": "Respublika günü" + }, + { + "date": "2018-06-15", + "name": "Ramazan Bayramı" + }, + { + "date": "2018-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2018-06-16", + "name": "Ramazan Bayramı" + }, + { + "date": "2018-06-18", + "name": "Ramazan Bayramı (əvəz gün)" + }, + { + "date": "2018-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2018-08-21", + "name": "Qurban Bayramı" + }, + { + "date": "2018-08-22", + "name": "Qurban Bayramı" + }, + { + "date": "2018-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2018-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2018-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2018-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2018-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2019-01-01", + "name": "Yeni il" + }, + { + "date": "2019-01-02", + "name": "Yeni il" + }, + { + "date": "2019-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2019-03-20", + "name": "Novruz" + }, + { + "date": "2019-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2019-05-28", + "name": "Respublika günü" + }, + { + "date": "2019-06-04", + "name": "Ramazan Bayramı" + }, + { + "date": "2019-06-05", + "name": "Ramazan Bayramı" + }, + { + "date": "2019-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2019-06-17", + "name": "Azərbaycan xalqının Milli Qurtuluş günü (əvəz gün)" + }, + { + "date": "2019-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2019-08-11", + "name": "Qurban Bayramı" + }, + { + "date": "2019-08-12", + "name": "Qurban Bayramı" + }, + { + "date": "2019-08-13", + "name": "Qurban Bayramı (əvəz gün)" + }, + { + "date": "2019-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2019-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2019-11-11", + "name": "Dövlət Bayrağı günü (əvəz gün)" + }, + { + "date": "2019-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2019-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2019-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2020-01-01", + "name": "Yeni il" + }, + { + "date": "2020-01-02", + "name": "Yeni il" + }, + { + "date": "2020-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2020-03-09", + "name": "Qadınlar günü (əvəz gün)" + }, + { + "date": "2020-03-20", + "name": "Novruz" + }, + { + "date": "2020-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2020-05-11", + "name": "Faşizm üzərində qələbə günü (əvəz gün)" + }, + { + "date": "2020-05-24", + "name": "Ramazan Bayramı" + }, + { + "date": "2020-05-25", + "name": "Ramazan Bayramı" + }, + { + "date": "2020-05-26", + "name": "Ramazan Bayramı (əvəz gün)" + }, + { + "date": "2020-05-28", + "name": "Respublika günü" + }, + { + "date": "2020-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2020-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2020-07-31", + "name": "Qurban Bayramı" + }, + { + "date": "2020-08-01", + "name": "Qurban Bayramı" + }, + { + "date": "2020-08-03", + "name": "Qurban Bayramı (əvəz gün)" + }, + { + "date": "2020-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2020-10-19", + "name": "Dövlət Müstəqilliyi günü (əvəz gün)" + }, + { + "date": "2020-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2020-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2020-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2020-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + }, + { + "date": "2021-01-01", + "name": "Yeni il" + }, + { + "date": "2021-01-02", + "name": "Yeni il" + }, + { + "date": "2021-01-04", + "name": "Yeni il (əvəz gün)" + }, + { + "date": "2021-03-08", + "name": "Qadınlar günü" + }, + { + "date": "2021-03-20", + "name": "Novruz" + }, + { + "date": "2021-05-09", + "name": "Faşizm üzərində qələbə günü" + }, + { + "date": "2021-05-10", + "name": "Faşizm üzərində qələbə günü (əvəz gün)" + }, + { + "date": "2021-05-13", + "name": "Ramazan Bayramı" + }, + { + "date": "2021-05-14", + "name": "Ramazan Bayramı" + }, + { + "date": "2021-05-28", + "name": "Respublika günü" + }, + { + "date": "2021-06-15", + "name": "Azərbaycan xalqının Milli Qurtuluş günü" + }, + { + "date": "2021-06-26", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü" + }, + { + "date": "2021-06-28", + "name": "Azərbaycan Respublikasının Silahlı Qüvvələri günü (əvəz gün)" + }, + { + "date": "2021-07-20", + "name": "Qurban Bayramı" + }, + { + "date": "2021-07-21", + "name": "Qurban Bayramı" + }, + { + "date": "2021-10-18", + "name": "Dövlət Müstəqilliyi günü" + }, + { + "date": "2021-11-09", + "name": "Dövlət Bayrağı günü" + }, + { + "date": "2021-11-12", + "name": "Konstitusiya günü" + }, + { + "date": "2021-11-17", + "name": "Milli Dirçəliş günü" + }, + { + "date": "2021-12-31", + "name": "Dünya azərbaycanlıların həmrəyliyi günü" + } + ] + }, + "BA": { + "name": "Bosna i Hercegovina", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2018-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2018-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2018-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2018-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2018-04-01", + "name": "Vaskrs" + }, + { + "date": "2018-04-02", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2018-04-08", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2018-05-01", + "name": "Radni dan" + }, + { + "date": "2018-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2018-05-31", + "name": "Tijelovo" + }, + { + "date": "2018-06-15", + "name": "Ramazanski bajram" + }, + { + "date": "2018-08-15", + "name": "Velika Gospa" + }, + { + "date": "2018-08-21", + "name": "Kurbanski bajram" + }, + { + "date": "2018-08-28", + "name": "Velika Gospa" + }, + { + "date": "2018-09-11", + "name": "Nova hidžretska godina" + }, + { + "date": "2018-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2018-11-02", + "name": "Dušni dan" + }, + { + "date": "2018-11-20", + "name": "Mevlud" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2019-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2019-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2019-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2019-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2019-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2019-04-21", + "name": "Vaskrs" + }, + { + "date": "2019-04-22", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2019-04-28", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2019-05-01", + "name": "Radni dan" + }, + { + "date": "2019-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2019-06-04", + "name": "Ramazanski bajram" + }, + { + "date": "2019-06-20", + "name": "Tijelovo" + }, + { + "date": "2019-08-11", + "name": "Kurbanski bajram" + }, + { + "date": "2019-08-15", + "name": "Velika Gospa" + }, + { + "date": "2019-08-28", + "name": "Velika Gospa" + }, + { + "date": "2019-08-31", + "name": "Nova hidžretska godina" + }, + { + "date": "2019-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2019-11-02", + "name": "Dušni dan" + }, + { + "date": "2019-11-09", + "name": "Mevlud" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2020-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2020-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2020-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2020-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2020-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2020-04-12", + "name": "Vaskrs" + }, + { + "date": "2020-04-13", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2020-04-19", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2020-05-01", + "name": "Radni dan" + }, + { + "date": "2020-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2020-05-24", + "name": "Ramazanski bajram" + }, + { + "date": "2020-06-11", + "name": "Tijelovo" + }, + { + "date": "2020-07-31", + "name": "Kurbanski bajram" + }, + { + "date": "2020-08-15", + "name": "Velika Gospa" + }, + { + "date": "2020-08-20", + "name": "Nova hidžretska godina" + }, + { + "date": "2020-08-28", + "name": "Velika Gospa" + }, + { + "date": "2020-10-29", + "name": "Mevlud" + }, + { + "date": "2020-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2020-11-02", + "name": "Dušni dan" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2021-01-01", + "name": "Novogodisnji dan" + }, + { + "date": "2021-01-02", + "name": "Drugi dan Nove Godine" + }, + { + "date": "2021-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2021-01-07", + "name": "Pravoslavni Božić" + }, + { + "date": "2021-01-14", + "name": "Pravoslavni novogodišnji dan" + }, + { + "date": "2021-04-04", + "name": "Vaskrs" + }, + { + "date": "2021-04-05", + "name": "Uskrsni ponedjeljak" + }, + { + "date": "2021-05-01", + "name": "Radni dan" + }, + { + "date": "2021-05-02", + "name": "Drugi dan Dana rada" + }, + { + "date": "2021-05-02", + "name": "Pravoslavni Vaskrs" + }, + { + "date": "2021-05-03", + "name": "Drugi dan Dana rada (zamjena dan)" + }, + { + "date": "2021-05-13", + "name": "Ramazanski bajram" + }, + { + "date": "2021-06-03", + "name": "Tijelovo" + }, + { + "date": "2021-07-20", + "name": "Kurbanski bajram" + }, + { + "date": "2021-08-09", + "name": "Nova hidžretska godina" + }, + { + "date": "2021-08-15", + "name": "Velika Gospa" + }, + { + "date": "2021-08-28", + "name": "Velika Gospa" + }, + { + "date": "2021-10-18", + "name": "Mevlud" + }, + { + "date": "2021-11-01", + "name": "Dita e të gjithë Shenjtorëve" + }, + { + "date": "2021-11-02", + "name": "Dušni dan" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Svetog Stjepana" + } + ] + }, + "BB": { + "name": "Barbados", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-28", + "name": "National Heroes Day" + }, + { + "date": "2018-05-01", + "name": "May Day" + }, + { + "date": "2018-05-20", + "name": "Pentecost" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-06", + "name": "Kadooment Day" + }, + { + "date": "2018-11-30", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-28", + "name": "National Heroes Day" + }, + { + "date": "2019-05-01", + "name": "May Day" + }, + { + "date": "2019-06-09", + "name": "Pentecost" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-05", + "name": "Kadooment Day" + }, + { + "date": "2019-11-30", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-28", + "name": "National Heroes Day" + }, + { + "date": "2020-05-01", + "name": "May Day" + }, + { + "date": "2020-05-31", + "name": "Pentecost" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-08-03", + "name": "Kadooment Day" + }, + { + "date": "2020-11-30", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-21", + "name": "Errol Barrow Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-28", + "name": "National Heroes Day" + }, + { + "date": "2021-05-01", + "name": "May Day" + }, + { + "date": "2021-05-23", + "name": "Pentecost" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-01", + "name": "Emancipation Day" + }, + { + "date": "2021-08-02", + "name": "Kadooment Day" + }, + { + "date": "2021-11-30", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" + } + ] + }, + "BE": { + "name": "Belgique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-01", + "name": "Pâques" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-21", + "name": "Fête nationale" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-21", + "name": "Pâques" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-21", + "name": "Fête nationale" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-12", + "name": "Pâques" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-21", + "name": "Fête nationale" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-04", + "name": "Pâques" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-21", + "name": "Fête nationale" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "BG": { + "name": "България", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова Година" + }, + { + "date": "2018-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2018-04-06", + "name": "Разпети петък" + }, + { + "date": "2018-04-08", + "name": "Великден" + }, + { + "date": "2018-04-09", + "name": "Велики понеделник" + }, + { + "date": "2018-05-01", + "name": "Ден на труда" + }, + { + "date": "2018-05-06", + "name": "Гергьовден" + }, + { + "date": "2018-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2018-09-06", + "name": "Ден на съединението" + }, + { + "date": "2018-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2018-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2018-12-25", + "name": "Коледа" + }, + { + "date": "2019-01-01", + "name": "Нова Година" + }, + { + "date": "2019-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2019-04-26", + "name": "Разпети петък" + }, + { + "date": "2019-04-28", + "name": "Великден" + }, + { + "date": "2019-04-29", + "name": "Велики понеделник" + }, + { + "date": "2019-05-01", + "name": "Ден на труда" + }, + { + "date": "2019-05-06", + "name": "Гергьовден" + }, + { + "date": "2019-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2019-09-06", + "name": "Ден на съединението" + }, + { + "date": "2019-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2019-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2019-12-25", + "name": "Коледа" + }, + { + "date": "2020-01-01", + "name": "Нова Година" + }, + { + "date": "2020-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2020-04-17", + "name": "Разпети петък" + }, + { + "date": "2020-04-19", + "name": "Великден" + }, + { + "date": "2020-04-20", + "name": "Велики понеделник" + }, + { + "date": "2020-05-01", + "name": "Ден на труда" + }, + { + "date": "2020-05-06", + "name": "Гергьовден" + }, + { + "date": "2020-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2020-09-06", + "name": "Ден на съединението" + }, + { + "date": "2020-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2020-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2020-12-25", + "name": "Коледа" + }, + { + "date": "2021-01-01", + "name": "Нова Година" + }, + { + "date": "2021-03-03", + "name": "Ден на Освобождението на България от Османската Империя" + }, + { + "date": "2021-04-30", + "name": "Разпети петък" + }, + { + "date": "2021-05-01", + "name": "Ден на труда" + }, + { + "date": "2021-05-02", + "name": "Великден" + }, + { + "date": "2021-05-03", + "name": "Велики понеделник" + }, + { + "date": "2021-05-06", + "name": "Гергьовден" + }, + { + "date": "2021-05-24", + "name": "Ден на азбуката, културата и просветата" + }, + { + "date": "2021-09-06", + "name": "Ден на съединението" + }, + { + "date": "2021-09-22", + "name": "Ден на независимостта" + }, + { + "date": "2021-12-24", + "name": "Бъдни вечер" + }, + { + "date": "2021-12-25", + "name": "Коледа" + } + ] + }, + "BI": { + "name": "République du Burundi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2018-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2018-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2019-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2019-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2020-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2020-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-02-05", + "name": "Jour de l'Unité" + }, + { + "date": "2021-04-06", + "name": "Jour de Ntaryamira" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-13", + "name": "Jour de Rwagasore" + }, + { + "date": "2021-10-21", + "name": "Jour de Ndadaye" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "BL": { + "name": "St. Barthélemy", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice de 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "BO": { + "name": "Bolivia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-10", + "name": "La Asunción" + }, + { + "date": "2018-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2018-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2018-08-06", + "name": "Día de la Patria" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-30", + "name": "La Asunción" + }, + { + "date": "2019-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2019-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2019-08-06", + "name": "Día de la Patria" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-21", + "name": "La Asunción" + }, + { + "date": "2020-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2020-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2020-08-06", + "name": "Día de la Patria" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-02", + "name": "Fiesta de la Virgen de Candelaria" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-13", + "name": "La Asunción" + }, + { + "date": "2021-06-21", + "name": "Año Nuevo Andino" + }, + { + "date": "2021-08-02", + "name": "Día de la Revolución Agraria, Productiva y Comunitaria" + }, + { + "date": "2021-08-06", + "name": "Día de la Patria" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "BQ": { + "name": "Caribisch Nederland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-03-30", + "name": "Goede Vrijdag" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2018-05-10", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2018-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-04-19", + "name": "Goede Vrijdag" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2019-05-30", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2019-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-04-10", + "name": "Goede Vrijdag" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2020-05-21", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2020-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-04-02", + "name": "Goede Vrijdag" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2021-05-13", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2021-12-15", + "name": "Koninkrijksdag" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + } + ] + }, + "BR": { + "name": "Brasil", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2018-09-07", + "name": "Dia da Independência" + }, + { + "date": "2018-10-07", + "name": "Dia de Eleição" + }, + { + "date": "2018-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2018-10-28", + "name": "Dia de Eleição" + }, + { + "date": "2018-11-02", + "name": "Dia de Finados" + }, + { + "date": "2018-11-15", + "name": "Proclamação da República" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2019-09-07", + "name": "Dia da Independência" + }, + { + "date": "2019-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2019-11-02", + "name": "Dia de Finados" + }, + { + "date": "2019-11-15", + "name": "Proclamação da República" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2020-09-07", + "name": "Dia da Independência" + }, + { + "date": "2020-10-04", + "name": "Dia de Eleição" + }, + { + "date": "2020-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2020-10-25", + "name": "Dia de Eleição" + }, + { + "date": "2020-11-02", + "name": "Dia de Finados" + }, + { + "date": "2020-11-15", + "name": "Proclamação da República" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-21", + "name": "Dia de Tiradentes" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-06-12", + "name": "Dia dos Namorados" + }, + { + "date": "2021-09-07", + "name": "Dia da Independência" + }, + { + "date": "2021-10-12", + "name": "Nossa Senhora Aparecida" + }, + { + "date": "2021-11-02", + "name": "Dia de Finados" + }, + { + "date": "2021-11-15", + "name": "Proclamação da República" + }, + { + "date": "2021-12-25", + "name": "Natal" + } + ] + }, + "BS": { + "name": "Bahamas", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-06-01", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2018-07-10", + "name": "Independence Day" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-06-07", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-07-10", + "name": "Independence Day" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2019-10-14", + "name": "National Heroes' Day (substitute day)" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-05", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2020-07-10", + "name": "Independence Day" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-10", + "name": "Majority Rule Day" + }, + { + "date": "2021-01-11", + "name": "Majority Rule Day (substitute day)" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-06-04", + "name": "Randol Fawkes Labour Day" + }, + { + "date": "2021-07-10", + "name": "Independence Day" + }, + { + "date": "2021-07-12", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-10-12", + "name": "National Heroes' Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "BW": { + "name": "Botswana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-10", + "name": "Ascension Day" + }, + { + "date": "2018-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2018-07-02", + "name": "Public Holiday" + }, + { + "date": "2018-07-16", + "name": "President’s Day" + }, + { + "date": "2018-07-17", + "name": "President’s Day Holiday" + }, + { + "date": "2018-09-30", + "name": "Botswana Day" + }, + { + "date": "2018-10-01", + "name": "Public Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Family Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-30", + "name": "Ascension Day" + }, + { + "date": "2019-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2019-07-15", + "name": "President’s Day" + }, + { + "date": "2019-07-16", + "name": "President’s Day Holiday" + }, + { + "date": "2019-09-30", + "name": "Botswana Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Family Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-21", + "name": "Ascension Day" + }, + { + "date": "2020-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2020-07-20", + "name": "President’s Day" + }, + { + "date": "2020-07-21", + "name": "President’s Day Holiday" + }, + { + "date": "2020-09-30", + "name": "Botswana Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Family Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "New Year's Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Ascension Day" + }, + { + "date": "2021-07-01", + "name": "Sir Seretse Khama Day" + }, + { + "date": "2021-07-19", + "name": "President’s Day" + }, + { + "date": "2021-07-20", + "name": "President’s Day Holiday" + }, + { + "date": "2021-09-30", + "name": "Botswana Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Family Day" + } + ] + }, + "BY": { + "name": "Рэспубліка Беларусь", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новы год" + }, + { + "date": "2018-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2018-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2018-04-17", + "name": "Радунiца" + }, + { + "date": "2018-05-01", + "name": "Дзень працы" + }, + { + "date": "2018-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2018-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2018-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2018-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2019-01-01", + "name": "Новы год" + }, + { + "date": "2019-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2019-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2019-05-01", + "name": "Дзень працы" + }, + { + "date": "2019-05-07", + "name": "Радунiца" + }, + { + "date": "2019-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2019-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2019-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2019-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2020-01-01", + "name": "Новы год" + }, + { + "date": "2020-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2020-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2020-04-28", + "name": "Радунiца" + }, + { + "date": "2020-05-01", + "name": "Дзень працы" + }, + { + "date": "2020-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2020-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2020-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2020-12-25", + "name": "Каляды каталiцкiя" + }, + { + "date": "2021-01-01", + "name": "Новы год" + }, + { + "date": "2021-01-07", + "name": "Каляды праваслаўныя" + }, + { + "date": "2021-03-08", + "name": "Мiжнародны жаночы дзень" + }, + { + "date": "2021-05-01", + "name": "Дзень працы" + }, + { + "date": "2021-05-09", + "name": "Дзень Перамогi" + }, + { + "date": "2021-05-11", + "name": "Радунiца" + }, + { + "date": "2021-07-03", + "name": "Дзень Незалежнасцi" + }, + { + "date": "2021-11-07", + "name": "Дзень Кастрычніцкай рэвалюцыі" + }, + { + "date": "2021-12-25", + "name": "Каляды каталiцкiя" + } + ] + }, + "BZ": { + "name": "Belize", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-12", + "name": "Baron Bliss Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-03-31", + "name": "Easter Saturday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2018-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2018-09-21", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "Day of the Americas" + }, + { + "date": "2018-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-11", + "name": "Baron Bliss Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-20", + "name": "Easter Saturday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-27", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2019-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2019-09-21", + "name": "Independence Day" + }, + { + "date": "2019-10-14", + "name": "Day of the Americas" + }, + { + "date": "2019-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Baron Bliss Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-11", + "name": "Easter Saturday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-25", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2020-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2020-09-21", + "name": "Independence Day" + }, + { + "date": "2020-10-12", + "name": "Day of the Americas" + }, + { + "date": "2020-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-08", + "name": "Baron Bliss Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-03", + "name": "Easter Saturday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Commonwealth Day, Sovereign's Day" + }, + { + "date": "2021-09-10", + "name": "Saint George's Caye Day, National Day" + }, + { + "date": "2021-09-21", + "name": "Independence Day" + }, + { + "date": "2021-10-11", + "name": "Day of the Americas" + }, + { + "date": "2021-11-19", + "name": "Garifuna Settlement Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "CA": { + "name": "Canada", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-05-21", + "name": "Victoria Day" + }, + { + "date": "2018-07-01", + "name": "Canada Day" + }, + { + "date": "2018-08-06", + "name": "Civic Holiday" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Thanksgiving" + }, + { + "date": "2018-11-11", + "name": "Remembrance Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-05-20", + "name": "Victoria Day" + }, + { + "date": "2019-07-01", + "name": "Canada Day" + }, + { + "date": "2019-08-05", + "name": "Civic Holiday" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Thanksgiving" + }, + { + "date": "2019-11-11", + "name": "Remembrance Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-05-18", + "name": "Victoria Day" + }, + { + "date": "2020-07-01", + "name": "Canada Day" + }, + { + "date": "2020-08-03", + "name": "Civic Holiday" + }, + { + "date": "2020-08-31", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Thanksgiving" + }, + { + "date": "2020-11-11", + "name": "Remembrance Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-05-24", + "name": "Victoria Day" + }, + { + "date": "2021-07-01", + "name": "Canada Day" + }, + { + "date": "2021-08-02", + "name": "Civic Holiday" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Thanksgiving" + }, + { + "date": "2021-11-11", + "name": "Remembrance Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "CC": { + "name": "Cocos (Keeling) Islands", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Australia Day" + }, + { + "date": "2018-02-16", + "name": "Chinese New Year" + }, + { + "date": "2018-02-17", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2018-02-19", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2018-03-20", + "name": "Labour Day" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-06", + "name": "Self Determination Day" + }, + { + "date": "2018-04-25", + "name": "Anzac Day" + }, + { + "date": "2018-06-15", + "name": "Hari Raya Puasa" + }, + { + "date": "2018-08-21", + "name": "Hari Raya Haji" + }, + { + "date": "2018-09-11", + "name": "Islamic New Year" + }, + { + "date": "2018-11-20", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Australia Day" + }, + { + "date": "2019-02-05", + "name": "Chinese New Year" + }, + { + "date": "2019-02-06", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2019-03-20", + "name": "Labour Day" + }, + { + "date": "2019-04-06", + "name": "Self Determination Day" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "Anzac Day" + }, + { + "date": "2019-06-04", + "name": "Hari Raya Puasa" + }, + { + "date": "2019-08-11", + "name": "Hari Raya Haji" + }, + { + "date": "2019-08-12", + "name": "Hari Raya Haji (substitute day)" + }, + { + "date": "2019-08-31", + "name": "Islamic New Year" + }, + { + "date": "2019-11-09", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-25", + "name": "Chinese New Year" + }, + { + "date": "2020-01-26", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2020-01-26", + "name": "Australia Day" + }, + { + "date": "2020-01-27", + "name": "Chinese New Year (substitute day)" + }, + { + "date": "2020-01-28", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2020-03-20", + "name": "Labour Day" + }, + { + "date": "2020-04-06", + "name": "Self Determination Day" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-25", + "name": "Anzac Day" + }, + { + "date": "2020-05-24", + "name": "Hari Raya Puasa" + }, + { + "date": "2020-05-25", + "name": "Hari Raya Puasa (substitute day)" + }, + { + "date": "2020-07-31", + "name": "Hari Raya Haji" + }, + { + "date": "2020-08-20", + "name": "Islamic New Year" + }, + { + "date": "2020-10-29", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Australia Day" + }, + { + "date": "2021-02-12", + "name": "Chinese New Year" + }, + { + "date": "2021-02-13", + "name": "Chinese New Year (2nd Day)" + }, + { + "date": "2021-02-15", + "name": "Chinese New Year (2nd Day) (substitute day)" + }, + { + "date": "2021-03-20", + "name": "Labour Day" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-06", + "name": "Self Determination Day" + }, + { + "date": "2021-04-25", + "name": "Anzac Day" + }, + { + "date": "2021-05-13", + "name": "Hari Raya Puasa" + }, + { + "date": "2021-07-20", + "name": "Hari Raya Haji" + }, + { + "date": "2021-08-09", + "name": "Islamic New Year" + }, + { + "date": "2021-10-18", + "name": "Hari Maulaud Nabi" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "CD": { + "name": "République démocratique du Congo", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2018-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2018-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2018-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2018-08-01", + "name": "Fête des parents" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2019-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2019-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2019-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2019-07-01", + "name": "Anniversaire de Indépendance (jour substitut)" + }, + { + "date": "2019-08-01", + "name": "Fête des parents" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2020-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2020-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2020-05-18", + "name": "Journée de la Révolution et des Forces Armées (jour substitut)" + }, + { + "date": "2020-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2020-08-01", + "name": "Fête des parents" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-04", + "name": "Martyrs de l'Indépendance" + }, + { + "date": "2021-01-16", + "name": "Journée du Héro National Laurent Désiré Kabila" + }, + { + "date": "2021-01-17", + "name": "Journée du Héro National Patrice Emery Lumumba" + }, + { + "date": "2021-01-18", + "name": "Journée du Héro National Patrice Emery Lumumba (jour substitut)" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-17", + "name": "Journée de la Révolution et des Forces Armées" + }, + { + "date": "2021-06-30", + "name": "Anniversaire de Indépendance" + }, + { + "date": "2021-08-01", + "name": "Fête des parents" + }, + { + "date": "2021-08-02", + "name": "Fête des parents (jour substitut)" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CF": { + "name": "République centrafricaine", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-30", + "name": "Journée de prière" + }, + { + "date": "2018-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-01", + "name": "Jour de la République" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-30", + "name": "Journée de prière" + }, + { + "date": "2019-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-01", + "name": "Jour de la République" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-30", + "name": "Journée de prière" + }, + { + "date": "2020-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-01", + "name": "Jour de la République" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-03-29", + "name": "Décès du Fondateur Barthélémy Boganda" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-30", + "name": "Journée de prière" + }, + { + "date": "2021-08-13", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-01", + "name": "Jour de la République" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CG": { + "name": "République du Congo", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-10", + "name": "Fête de la commémoration de la conférence nationale souveraine" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-28", + "name": "Journée nationale de la République" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CH": { + "name": "Schweiz", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-01", + "name": "Ostersonntag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-10", + "name": "Auffahrt" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-08-01", + "name": "Bundesfeier" + }, + { + "date": "2018-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2018-12-26", + "name": "Stephanstag" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-21", + "name": "Ostersonntag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-30", + "name": "Auffahrt" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-08-01", + "name": "Bundesfeier" + }, + { + "date": "2019-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2019-12-26", + "name": "Stephanstag" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-12", + "name": "Ostersonntag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-21", + "name": "Auffahrt" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-08-01", + "name": "Bundesfeier" + }, + { + "date": "2020-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2020-12-26", + "name": "Stephanstag" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-04", + "name": "Ostersonntag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-13", + "name": "Auffahrt" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-08-01", + "name": "Bundesfeier" + }, + { + "date": "2021-12-25", + "name": "Weihnachtstag" + }, + { + "date": "2021-12-26", + "name": "Stephanstag" + } + ] + }, + "CL": { + "name": "Chile", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2018-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2018-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2018-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-11-02", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2019-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2019-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2019-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2019-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2020-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2020-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2020-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-21", + "name": "Día de las Glorias Navales" + }, + { + "date": "2021-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-16", + "name": "Virgen del Carmen" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-18", + "name": "Fiestas Patrias" + }, + { + "date": "2021-09-19", + "name": "Día de las Glorias del Ejército" + }, + { + "date": "2021-10-12", + "name": "Día del Descubrimiento de Dos Mundos" + }, + { + "date": "2021-10-31", + "name": "Día Nacional de las Iglesias Evangélicas y Protestantes" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "CM": { + "name": "Cameroun", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-02-11", + "name": "Fête de la Jeunesse" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-20", + "name": "Fête nationale ou de l'unité" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "CN": { + "name": "中华人民共和国", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "元旦" + }, + { + "date": "2018-02-16", + "name": "春节" + }, + { + "date": "2018-03-08", + "name": "国际妇女节" + }, + { + "date": "2018-04-05", + "name": "清明节 清明節" + }, + { + "date": "2018-05-01", + "name": "劳动节" + }, + { + "date": "2018-05-04", + "name": "青年节" + }, + { + "date": "2018-06-01", + "name": "六一儿童节" + }, + { + "date": "2018-06-18", + "name": "端午节" + }, + { + "date": "2018-08-01", + "name": "建军节" + }, + { + "date": "2018-09-24", + "name": "中秋节" + }, + { + "date": "2018-10-01", + "name": "国庆节" + }, + { + "date": "2019-01-01", + "name": "元旦" + }, + { + "date": "2019-02-05", + "name": "春节" + }, + { + "date": "2019-03-08", + "name": "国际妇女节" + }, + { + "date": "2019-04-05", + "name": "清明节 清明節" + }, + { + "date": "2019-05-01", + "name": "劳动节" + }, + { + "date": "2019-05-04", + "name": "青年节" + }, + { + "date": "2019-06-01", + "name": "六一儿童节" + }, + { + "date": "2019-06-07", + "name": "端午节" + }, + { + "date": "2019-08-01", + "name": "建军节" + }, + { + "date": "2019-09-13", + "name": "中秋节" + }, + { + "date": "2019-10-01", + "name": "国庆节" + }, + { + "date": "2020-01-01", + "name": "元旦" + }, + { + "date": "2020-01-25", + "name": "春节" + }, + { + "date": "2020-03-08", + "name": "国际妇女节" + }, + { + "date": "2020-04-04", + "name": "清明节 清明節" + }, + { + "date": "2020-05-01", + "name": "劳动节" + }, + { + "date": "2020-05-04", + "name": "青年节" + }, + { + "date": "2020-06-01", + "name": "六一儿童节" + }, + { + "date": "2020-06-25", + "name": "端午节" + }, + { + "date": "2020-08-01", + "name": "建军节" + }, + { + "date": "2020-10-01", + "name": "国庆节" + }, + { + "date": "2020-10-01", + "name": "中秋节" + }, + { + "date": "2021-01-01", + "name": "元旦" + }, + { + "date": "2021-02-12", + "name": "春节" + }, + { + "date": "2021-03-08", + "name": "国际妇女节" + }, + { + "date": "2021-04-04", + "name": "清明节 清明節" + }, + { + "date": "2021-05-01", + "name": "劳动节" + }, + { + "date": "2021-05-04", + "name": "青年节" + }, + { + "date": "2021-06-01", + "name": "六一儿童节" + }, + { + "date": "2021-06-14", + "name": "端午节" + }, + { + "date": "2021-08-01", + "name": "建军节" + }, + { + "date": "2021-09-21", + "name": "中秋节" + }, + { + "date": "2021-10-01", + "name": "国庆节" + } + ] + }, + "CO": { + "name": "Colombia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-08", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-03-19", + "name": "San José" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-14", + "name": "La Asunción" + }, + { + "date": "2018-06-04", + "name": "Corpus Christi" + }, + { + "date": "2018-06-11", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2018-07-02", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2018-08-20", + "name": "Asunción" + }, + { + "date": "2018-10-15", + "name": "Día de la Raza" + }, + { + "date": "2018-11-05", + "name": "Todos los Santos" + }, + { + "date": "2018-11-12", + "name": "Independencia de Cartagena" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-07", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-03-25", + "name": "San José" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-03", + "name": "La Asunción" + }, + { + "date": "2019-06-24", + "name": "Corpus Christi" + }, + { + "date": "2019-07-01", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2019-07-01", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2019-08-19", + "name": "Asunción" + }, + { + "date": "2019-10-14", + "name": "Día de la Raza" + }, + { + "date": "2019-11-04", + "name": "Todos los Santos" + }, + { + "date": "2019-11-11", + "name": "Independencia de Cartagena" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-03-23", + "name": "San José" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-25", + "name": "La Asunción" + }, + { + "date": "2020-06-15", + "name": "Corpus Christi" + }, + { + "date": "2020-06-22", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2020-08-17", + "name": "Asunción" + }, + { + "date": "2020-10-12", + "name": "Día de la Raza" + }, + { + "date": "2020-11-02", + "name": "Todos los Santos" + }, + { + "date": "2020-11-16", + "name": "Independencia de Cartagena" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-11", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-03-22", + "name": "San José" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-17", + "name": "La Asunción" + }, + { + "date": "2021-06-07", + "name": "Corpus Christi" + }, + { + "date": "2021-06-14", + "name": "Sagrado Corazón de Jesús" + }, + { + "date": "2021-07-05", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-20", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-07", + "name": "Batalla de Boyacá" + }, + { + "date": "2021-08-16", + "name": "Asunción" + }, + { + "date": "2021-10-18", + "name": "Día de la Raza" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-11-15", + "name": "Independencia de Cartagena" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CR": { + "name": "Costa Rica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2018-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2018-08-15", + "name": "Día de la Madre" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-12", + "name": "Día de la Raza" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2019-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2019-08-15", + "name": "Día de la Madre" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-12", + "name": "Día de la Raza" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2020-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2020-08-15", + "name": "Día de la Madre" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-09", + "name": "Día de la Raza" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-11", + "name": "Gesta Heroica de Juan Santamaría" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-25", + "name": "Anexión del Partido de Nicoya" + }, + { + "date": "2021-08-02", + "name": "Día de la Virgen de los Ángeles" + }, + { + "date": "2021-08-15", + "name": "Día de la Madre" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-12", + "name": "Día de la Raza" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CU": { + "name": "Cuba", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2018-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2018-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2018-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2018-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2019-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2019-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2019-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2019-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2020-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2020-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2020-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2020-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Triunfo de la Revolución" + }, + { + "date": "2021-01-02", + "name": "Día de Victoria de las Fuerzas Armadas" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-25", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2021-07-26", + "name": "Día de la Rebeldía Nacional" + }, + { + "date": "2021-07-27", + "name": "Conmemoración del asalto a Moncada" + }, + { + "date": "2021-10-10", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "CW": { + "name": "Curaçao", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-02-12", + "name": "Carnavalmaandag" + }, + { + "date": "2018-03-30", + "name": "Goede Vrijdag" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2018-05-10", + "name": "Hemelvaartsdag" + }, + { + "date": "2018-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2018-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2018-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-03-04", + "name": "Carnavalmaandag" + }, + { + "date": "2019-04-19", + "name": "Goede Vrijdag" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2019-05-30", + "name": "Hemelvaartsdag" + }, + { + "date": "2019-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2019-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-02-24", + "name": "Carnavalmaandag" + }, + { + "date": "2020-04-10", + "name": "Goede Vrijdag" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2020-05-21", + "name": "Hemelvaartsdag" + }, + { + "date": "2020-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2020-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-02-15", + "name": "Carnavalmaandag" + }, + { + "date": "2021-04-02", + "name": "Goede Vrijdag" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-01", + "name": "Dag van de Arbeid" + }, + { + "date": "2021-05-13", + "name": "Hemelvaartsdag" + }, + { + "date": "2021-07-02", + "name": "Dag van het volkslied en de Vlag" + }, + { + "date": "2021-10-10", + "name": "Dag van het land Curaçao" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-12-31", + "name": "Oudejaarsavond" + } + ] + }, + "CY": { + "name": "Κύπρος", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2018-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2018-02-20", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2018-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2018-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2018-04-06", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2018-04-08", + "name": "Πάσχα" + }, + { + "date": "2018-04-09", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2018-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2018-05-27", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2018-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2018-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2018-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2018-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2018-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2019-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2019-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2019-03-12", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2019-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2019-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2019-04-26", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2019-04-28", + "name": "Πάσχα" + }, + { + "date": "2019-04-29", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2019-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2019-06-16", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2019-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2019-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2019-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2019-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2019-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2020-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2020-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2020-03-03", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2020-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2020-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2020-04-17", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2020-04-19", + "name": "Πάσχα" + }, + { + "date": "2020-04-20", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2020-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2020-06-07", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2020-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2020-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2020-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2020-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2020-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2021-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2021-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2021-03-16", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2021-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2021-04-01", + "name": "Κύπρος Εθνική Εορτή" + }, + { + "date": "2021-04-30", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2021-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2021-05-02", + "name": "Πάσχα" + }, + { + "date": "2021-05-03", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2021-06-20", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2021-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2021-10-01", + "name": "Ημέρα της Ανεξαρτησίας Κύπρος" + }, + { + "date": "2021-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2021-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2021-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + } + ] + }, + "CZ": { + "name": "Česká republika", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2018-03-30", + "name": "Velikonoční pátek" + }, + { + "date": "2018-04-01", + "name": "Neděle velikonoční" + }, + { + "date": "2018-04-02", + "name": "Velikonoční pondělí" + }, + { + "date": "2018-05-01", + "name": "Svátek práce" + }, + { + "date": "2018-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2018-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2018-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2018-09-28", + "name": "Den české státnosti" + }, + { + "date": "2018-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2018-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2018-12-24", + "name": "Štědrý den" + }, + { + "date": "2018-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2018-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2019-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2019-04-19", + "name": "Velikonoční pátek" + }, + { + "date": "2019-04-21", + "name": "Neděle velikonoční" + }, + { + "date": "2019-04-22", + "name": "Velikonoční pondělí" + }, + { + "date": "2019-05-01", + "name": "Svátek práce" + }, + { + "date": "2019-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2019-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2019-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2019-09-28", + "name": "Den české státnosti" + }, + { + "date": "2019-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2019-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2019-12-24", + "name": "Štědrý den" + }, + { + "date": "2019-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2019-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2020-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2020-04-10", + "name": "Velikonoční pátek" + }, + { + "date": "2020-04-12", + "name": "Neděle velikonoční" + }, + { + "date": "2020-04-13", + "name": "Velikonoční pondělí" + }, + { + "date": "2020-05-01", + "name": "Svátek práce" + }, + { + "date": "2020-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2020-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2020-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2020-09-28", + "name": "Den české státnosti" + }, + { + "date": "2020-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2020-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2020-12-24", + "name": "Štědrý den" + }, + { + "date": "2020-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2020-12-26", + "name": "2. svátek vánoční" + }, + { + "date": "2021-01-01", + "name": "Nový rok; Den obnovy samostatného českého státu" + }, + { + "date": "2021-04-02", + "name": "Velikonoční pátek" + }, + { + "date": "2021-04-04", + "name": "Neděle velikonoční" + }, + { + "date": "2021-04-05", + "name": "Velikonoční pondělí" + }, + { + "date": "2021-05-01", + "name": "Svátek práce" + }, + { + "date": "2021-05-08", + "name": "Den vítězství or Den osvobození" + }, + { + "date": "2021-07-05", + "name": "Den slovanských věrozvěstů Cyrila a Metoděje" + }, + { + "date": "2021-07-06", + "name": "Den upálení mistra Jana Husa" + }, + { + "date": "2021-09-28", + "name": "Den české státnosti" + }, + { + "date": "2021-10-28", + "name": "Den vzniku samostatného československého státu" + }, + { + "date": "2021-11-17", + "name": "Den boje za svobodu a demokracii" + }, + { + "date": "2021-12-24", + "name": "Štědrý den" + }, + { + "date": "2021-12-25", + "name": "1. svátek vánoční" + }, + { + "date": "2021-12-26", + "name": "2. svátek vánoční" + } + ] + }, + "DE": { + "name": "Deutschland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-02-13", + "name": "Faschingsdienstag" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Maifeiertag" + }, + { + "date": "2018-05-10", + "name": "Christi Himmelfahrt" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2018-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-03-05", + "name": "Faschingsdienstag" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Maifeiertag" + }, + { + "date": "2019-05-30", + "name": "Christi Himmelfahrt" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2019-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-02-25", + "name": "Faschingsdienstag" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Maifeiertag" + }, + { + "date": "2020-05-21", + "name": "Christi Himmelfahrt" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2020-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-02-16", + "name": "Faschingsdienstag" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Maifeiertag" + }, + { + "date": "2021-05-13", + "name": "Christi Himmelfahrt" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-10-03", + "name": "Tag der Deutschen Einheit" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "1. Weihnachtstag" + }, + { + "date": "2021-12-26", + "name": "2. Weihnachtstag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "DK": { + "name": "Danmark", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nytår" + }, + { + "date": "2018-03-29", + "name": "Skærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Påskesøndag" + }, + { + "date": "2018-04-02", + "name": "Anden påskedag" + }, + { + "date": "2018-04-27", + "name": "Store Bededag" + }, + { + "date": "2018-05-10", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2018-05-20", + "name": "Pinsedag" + }, + { + "date": "2018-05-21", + "name": "2. Pinsedag" + }, + { + "date": "2018-12-25", + "name": "1. Juledag" + }, + { + "date": "2018-12-26", + "name": "2. Juledag" + }, + { + "date": "2019-01-01", + "name": "Nytår" + }, + { + "date": "2019-04-18", + "name": "Skærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Påskesøndag" + }, + { + "date": "2019-04-22", + "name": "Anden påskedag" + }, + { + "date": "2019-05-17", + "name": "Store Bededag" + }, + { + "date": "2019-05-30", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Pinsedag" + }, + { + "date": "2019-06-10", + "name": "2. Pinsedag" + }, + { + "date": "2019-12-25", + "name": "1. Juledag" + }, + { + "date": "2019-12-26", + "name": "2. Juledag" + }, + { + "date": "2020-01-01", + "name": "Nytår" + }, + { + "date": "2020-04-09", + "name": "Skærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Påskesøndag" + }, + { + "date": "2020-04-13", + "name": "Anden påskedag" + }, + { + "date": "2020-05-08", + "name": "Store Bededag" + }, + { + "date": "2020-05-21", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Pinsedag" + }, + { + "date": "2020-06-01", + "name": "2. Pinsedag" + }, + { + "date": "2020-12-25", + "name": "1. Juledag" + }, + { + "date": "2020-12-26", + "name": "2. Juledag" + }, + { + "date": "2021-01-01", + "name": "Nytår" + }, + { + "date": "2021-04-01", + "name": "Skærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Påskesøndag" + }, + { + "date": "2021-04-05", + "name": "Anden påskedag" + }, + { + "date": "2021-04-30", + "name": "Store Bededag" + }, + { + "date": "2021-05-13", + "name": "Kristi Himmelfartsdag" + }, + { + "date": "2021-05-23", + "name": "Pinsedag" + }, + { + "date": "2021-05-24", + "name": "2. Pinsedag" + }, + { + "date": "2021-12-25", + "name": "1. Juledag" + }, + { + "date": "2021-12-26", + "name": "2. Juledag" + } + ] + }, + "DM": { + "name": "Dominica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-12", + "name": "Carnival Monday" + }, + { + "date": "2018-02-13", + "name": "Carnival Tuesday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-11-03", + "name": "Independence Day" + }, + { + "date": "2018-11-05", + "name": "National Day of Community Service" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-04", + "name": "Carnival Monday" + }, + { + "date": "2019-03-05", + "name": "Carnival Tuesday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-11-04", + "name": "Independence Day" + }, + { + "date": "2019-11-05", + "name": "National Day of Community Service" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-24", + "name": "Carnival Monday" + }, + { + "date": "2020-02-25", + "name": "Carnival Tuesday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-11-03", + "name": "Independence Day" + }, + { + "date": "2020-11-04", + "name": "National Day of Community Service" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-15", + "name": "Carnival Monday" + }, + { + "date": "2021-02-16", + "name": "Carnival Tuesday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-11-03", + "name": "Independence Day" + }, + { + "date": "2021-11-04", + "name": "National Day of Community Service" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "DO": { + "name": "República Dominicana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2018-01-29", + "name": "Día de Duarte" + }, + { + "date": "2018-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-30", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2018-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2018-11-12", + "name": "Día de la Constitución" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2019-01-26", + "name": "Día de Duarte" + }, + { + "date": "2019-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-29", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2019-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2019-11-11", + "name": "Día de la Constitución" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2020-01-26", + "name": "Día de Duarte" + }, + { + "date": "2020-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-04", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2020-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2020-11-09", + "name": "Día de la Constitución" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-04", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-01-21", + "name": "Nuestra Señora de la Altagracia" + }, + { + "date": "2021-01-25", + "name": "Día de Duarte" + }, + { + "date": "2021-02-27", + "name": "Día de la Independencia" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-08-16", + "name": "Día de la Restauración" + }, + { + "date": "2021-09-24", + "name": "Nuestra Señora de las Mercedes" + }, + { + "date": "2021-11-08", + "name": "Día de la Constitución" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "EC": { + "name": "Ecuador", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2018-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2018-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2019-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2019-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2020-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2020-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-24", + "name": "Batalla del Pichincha" + }, + { + "date": "2021-08-10", + "name": "Día del Primer Grito de Independencia de Quito" + }, + { + "date": "2021-09-10", + "name": "Independencia de Guayaquil" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-11-03", + "name": "Independencia de Cuenca" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "EE": { + "name": "Eesti", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "uusaasta" + }, + { + "date": "2018-03-30", + "name": "suur reede" + }, + { + "date": "2018-04-01", + "name": "lihavõtted" + }, + { + "date": "2018-05-01", + "name": "kevadpüha" + }, + { + "date": "2018-05-20", + "name": "nelipühade 1. püha" + }, + { + "date": "2018-06-23", + "name": "võidupüha" + }, + { + "date": "2018-06-24", + "name": "jaanipäev" + }, + { + "date": "2018-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2018-12-24", + "name": "jõululaupäev" + }, + { + "date": "2018-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2018-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2019-01-01", + "name": "uusaasta" + }, + { + "date": "2019-04-19", + "name": "suur reede" + }, + { + "date": "2019-04-21", + "name": "lihavõtted" + }, + { + "date": "2019-05-01", + "name": "kevadpüha" + }, + { + "date": "2019-06-09", + "name": "nelipühade 1. püha" + }, + { + "date": "2019-06-23", + "name": "võidupüha" + }, + { + "date": "2019-06-24", + "name": "jaanipäev" + }, + { + "date": "2019-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2019-12-24", + "name": "jõululaupäev" + }, + { + "date": "2019-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2019-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2020-01-01", + "name": "uusaasta" + }, + { + "date": "2020-04-10", + "name": "suur reede" + }, + { + "date": "2020-04-12", + "name": "lihavõtted" + }, + { + "date": "2020-05-01", + "name": "kevadpüha" + }, + { + "date": "2020-05-31", + "name": "nelipühade 1. püha" + }, + { + "date": "2020-06-23", + "name": "võidupüha" + }, + { + "date": "2020-06-24", + "name": "jaanipäev" + }, + { + "date": "2020-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2020-12-24", + "name": "jõululaupäev" + }, + { + "date": "2020-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2020-12-26", + "name": "teine jõulupüha" + }, + { + "date": "2021-01-01", + "name": "uusaasta" + }, + { + "date": "2021-04-02", + "name": "suur reede" + }, + { + "date": "2021-04-04", + "name": "lihavõtted" + }, + { + "date": "2021-05-01", + "name": "kevadpüha" + }, + { + "date": "2021-05-23", + "name": "nelipühade 1. püha" + }, + { + "date": "2021-06-23", + "name": "võidupüha" + }, + { + "date": "2021-06-24", + "name": "jaanipäev" + }, + { + "date": "2021-08-20", + "name": "taasiseseisvumispäev" + }, + { + "date": "2021-12-24", + "name": "jõululaupäev" + }, + { + "date": "2021-12-25", + "name": "esimene jõulupüha" + }, + { + "date": "2021-12-26", + "name": "teine jõulupüha" + } + ] + }, + "ES": { + "name": "España", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2018-03-19", + "name": "San José" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2019-01-07", + "name": "Día de los Reyes Magos (día sustituto)" + }, + { + "date": "2019-03-19", + "name": "San José" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-09", + "name": "La inmaculada concepción (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2020-03-19", + "name": "San José" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-11-02", + "name": "Todos los Santos (día sustituto)" + }, + { + "date": "2020-12-07", + "name": "Día de la Constitución Española" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-06", + "name": "Día de los Reyes Magos" + }, + { + "date": "2021-03-19", + "name": "San José" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-08-16", + "name": "Asunción (día sustituto)" + }, + { + "date": "2021-10-12", + "name": "Fiesta Nacional de España" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-06", + "name": "Día de la Constitución Española" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "ET": { + "name": "ኢትዮጵያ", + "bank_holidays": [ + { + "date": "2018-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2018-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2018-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2018-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2018-04-06", + "name": "ስቅለት" + }, + { + "date": "2018-04-08", + "name": "ፋሲካ" + }, + { + "date": "2018-05-16", + "name": "ረመዳን" + }, + { + "date": "2018-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2018-06-15", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2018-08-21", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2018-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2018-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2018-11-20", + "name": "መውሊድ" + }, + { + "date": "2019-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2019-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2019-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2019-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2019-04-26", + "name": "ስቅለት" + }, + { + "date": "2019-04-28", + "name": "ፋሲካ" + }, + { + "date": "2019-05-06", + "name": "ረመዳን" + }, + { + "date": "2019-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2019-06-04", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2019-08-11", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2019-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2019-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2019-11-09", + "name": "መውሊድ" + }, + { + "date": "2020-01-07", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2020-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2020-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2020-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2020-04-17", + "name": "ስቅለት" + }, + { + "date": "2020-04-19", + "name": "ፋሲካ" + }, + { + "date": "2020-04-24", + "name": "ረመዳን" + }, + { + "date": "2020-05-24", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2020-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2020-07-31", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2020-09-12", + "name": "እንቁጣጣሽ" + }, + { + "date": "2020-09-28", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2020-10-29", + "name": "መውሊድ" + }, + { + "date": "2021-01-06", + "name": "ልደተ-ለእግዚእነ/ ገና" + }, + { + "date": "2021-01-19", + "name": "ብርሐነ ጥምቀት" + }, + { + "date": "2021-03-02", + "name": "የዓድዋ ድል በዓል" + }, + { + "date": "2021-03-28", + "name": "የቀይ ሽብር መታሰቢያ ቀን" + }, + { + "date": "2021-04-13", + "name": "ረመዳን" + }, + { + "date": "2021-04-30", + "name": "ስቅለት" + }, + { + "date": "2021-05-02", + "name": "ፋሲካ" + }, + { + "date": "2021-05-13", + "name": "ዒድ አል ፈጥር" + }, + { + "date": "2021-05-28", + "name": "ደርግ የወደቀበት ቀን" + }, + { + "date": "2021-07-20", + "name": "ዒድ አል አድሐ" + }, + { + "date": "2021-09-11", + "name": "እንቁጣጣሽ" + }, + { + "date": "2021-09-27", + "name": "ብርሐነ-መስቀል" + }, + { + "date": "2021-10-18", + "name": "መውሊድ" + } + ] + }, + "FI": { + "name": "Suomi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2018-01-06", + "name": "Loppiainen" + }, + { + "date": "2018-03-30", + "name": "Pitkäperjantai" + }, + { + "date": "2018-04-01", + "name": "Pääsiäispäivä" + }, + { + "date": "2018-04-02", + "name": "2. pääsiäispäivä" + }, + { + "date": "2018-05-01", + "name": "Vappu" + }, + { + "date": "2018-05-10", + "name": "Helatorstai" + }, + { + "date": "2018-05-20", + "name": "Helluntaipäivä" + }, + { + "date": "2018-06-22", + "name": "Juhannusaatto" + }, + { + "date": "2018-06-23", + "name": "Juhannuspäivä" + }, + { + "date": "2018-11-03", + "name": "Pyhäinpäivä" + }, + { + "date": "2018-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2018-12-24", + "name": "Jouluaatto" + }, + { + "date": "2018-12-25", + "name": "Joulupäivä" + }, + { + "date": "2018-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2018-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2019-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2019-01-06", + "name": "Loppiainen" + }, + { + "date": "2019-04-19", + "name": "Pitkäperjantai" + }, + { + "date": "2019-04-21", + "name": "Pääsiäispäivä" + }, + { + "date": "2019-04-22", + "name": "2. pääsiäispäivä" + }, + { + "date": "2019-05-01", + "name": "Vappu" + }, + { + "date": "2019-05-30", + "name": "Helatorstai" + }, + { + "date": "2019-06-09", + "name": "Helluntaipäivä" + }, + { + "date": "2019-06-21", + "name": "Juhannusaatto" + }, + { + "date": "2019-06-22", + "name": "Juhannuspäivä" + }, + { + "date": "2019-11-02", + "name": "Pyhäinpäivä" + }, + { + "date": "2019-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2019-12-24", + "name": "Jouluaatto" + }, + { + "date": "2019-12-25", + "name": "Joulupäivä" + }, + { + "date": "2019-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2019-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2020-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2020-01-06", + "name": "Loppiainen" + }, + { + "date": "2020-04-10", + "name": "Pitkäperjantai" + }, + { + "date": "2020-04-12", + "name": "Pääsiäispäivä" + }, + { + "date": "2020-04-13", + "name": "2. pääsiäispäivä" + }, + { + "date": "2020-05-01", + "name": "Vappu" + }, + { + "date": "2020-05-21", + "name": "Helatorstai" + }, + { + "date": "2020-05-31", + "name": "Helluntaipäivä" + }, + { + "date": "2020-06-19", + "name": "Juhannusaatto" + }, + { + "date": "2020-06-20", + "name": "Juhannuspäivä" + }, + { + "date": "2020-10-31", + "name": "Pyhäinpäivä" + }, + { + "date": "2020-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2020-12-24", + "name": "Jouluaatto" + }, + { + "date": "2020-12-25", + "name": "Joulupäivä" + }, + { + "date": "2020-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2020-12-31", + "name": "Uudenvuodenaatto" + }, + { + "date": "2021-01-01", + "name": "Uudenvuodenpäivä" + }, + { + "date": "2021-01-06", + "name": "Loppiainen" + }, + { + "date": "2021-04-02", + "name": "Pitkäperjantai" + }, + { + "date": "2021-04-04", + "name": "Pääsiäispäivä" + }, + { + "date": "2021-04-05", + "name": "2. pääsiäispäivä" + }, + { + "date": "2021-05-01", + "name": "Vappu" + }, + { + "date": "2021-05-13", + "name": "Helatorstai" + }, + { + "date": "2021-05-23", + "name": "Helluntaipäivä" + }, + { + "date": "2021-06-25", + "name": "Juhannusaatto" + }, + { + "date": "2021-06-26", + "name": "Juhannuspäivä" + }, + { + "date": "2021-11-06", + "name": "Pyhäinpäivä" + }, + { + "date": "2021-12-06", + "name": "Itsenäisyyspäivä" + }, + { + "date": "2021-12-24", + "name": "Jouluaatto" + }, + { + "date": "2021-12-25", + "name": "Joulupäivä" + }, + { + "date": "2021-12-26", + "name": "2. joulupäivä" + }, + { + "date": "2021-12-31", + "name": "Uudenvuodenaatto" + } + ] + }, + "FO": { + "name": "Føroyar", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2018-03-29", + "name": "Skírhósdagur" + }, + { + "date": "2018-03-30", + "name": "Langafríggjadagur" + }, + { + "date": "2018-04-01", + "name": "Páskadagur" + }, + { + "date": "2018-04-02", + "name": "Annar páskadagur" + }, + { + "date": "2018-04-24", + "name": "Flaggdagur" + }, + { + "date": "2018-04-27", + "name": "Dýri biðidagur" + }, + { + "date": "2018-05-10", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2018-05-20", + "name": "Hvítusunnudagur" + }, + { + "date": "2018-05-21", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2018-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2018-12-24", + "name": "Jólaaftan" + }, + { + "date": "2018-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2018-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2018-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2019-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2019-04-18", + "name": "Skírhósdagur" + }, + { + "date": "2019-04-19", + "name": "Langafríggjadagur" + }, + { + "date": "2019-04-21", + "name": "Páskadagur" + }, + { + "date": "2019-04-22", + "name": "Annar páskadagur" + }, + { + "date": "2019-04-24", + "name": "Flaggdagur" + }, + { + "date": "2019-05-17", + "name": "Dýri biðidagur" + }, + { + "date": "2019-05-30", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2019-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2019-06-09", + "name": "Hvítusunnudagur" + }, + { + "date": "2019-06-10", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2019-12-24", + "name": "Jólaaftan" + }, + { + "date": "2019-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2019-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2019-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2020-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2020-04-09", + "name": "Skírhósdagur" + }, + { + "date": "2020-04-10", + "name": "Langafríggjadagur" + }, + { + "date": "2020-04-12", + "name": "Páskadagur" + }, + { + "date": "2020-04-13", + "name": "Annar páskadagur" + }, + { + "date": "2020-04-24", + "name": "Flaggdagur" + }, + { + "date": "2020-05-08", + "name": "Dýri biðidagur" + }, + { + "date": "2020-05-21", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2020-05-31", + "name": "Hvítusunnudagur" + }, + { + "date": "2020-06-01", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2020-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2020-12-24", + "name": "Jólaaftan" + }, + { + "date": "2020-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2020-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2020-12-31", + "name": "Nýggjársaftan" + }, + { + "date": "2021-01-01", + "name": "Nýggjársdagur" + }, + { + "date": "2021-04-01", + "name": "Skírhósdagur" + }, + { + "date": "2021-04-02", + "name": "Langafríggjadagur" + }, + { + "date": "2021-04-04", + "name": "Páskadagur" + }, + { + "date": "2021-04-05", + "name": "Annar páskadagur" + }, + { + "date": "2021-04-24", + "name": "Flaggdagur" + }, + { + "date": "2021-04-30", + "name": "Dýri biðidagur" + }, + { + "date": "2021-05-13", + "name": "Kristi Himmalsferðardagur" + }, + { + "date": "2021-05-23", + "name": "Hvítusunnudagur" + }, + { + "date": "2021-05-24", + "name": "Annar hvítusunnudagur" + }, + { + "date": "2021-06-05", + "name": "Grundlógardagur" + }, + { + "date": "2021-12-24", + "name": "Jólaaftan" + }, + { + "date": "2021-12-25", + "name": "Fyrsti jóladagur" + }, + { + "date": "2021-12-26", + "name": "Fyrsti gerandisdagur eftir jóladag" + }, + { + "date": "2021-12-31", + "name": "Nýggjársaftan" + } + ] + }, + "FR": { + "name": "France", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GA": { + "name": "Gabon", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-04-17", + "name": "Journée des droits de la femme" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-08-16", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GB": { + "name": "United Kingdom", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GD": { + "name": "Grenada", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-07", + "name": "Independence Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-21", + "name": "Whit Monday" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-08-06", + "name": "Emancipation Day" + }, + { + "date": "2018-08-13", + "name": "Carnival Monday" + }, + { + "date": "2018-08-14", + "name": "Carnival Tuesday" + }, + { + "date": "2018-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-02-07", + "name": "Independence Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-10", + "name": "Whit Monday" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-05", + "name": "Emancipation Day" + }, + { + "date": "2019-08-12", + "name": "Carnival Monday" + }, + { + "date": "2019-08-13", + "name": "Carnival Tuesday" + }, + { + "date": "2019-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-07", + "name": "Independence Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-06-01", + "name": "Whit Monday" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-03", + "name": "Emancipation Day" + }, + { + "date": "2020-08-10", + "name": "Carnival Monday" + }, + { + "date": "2020-08-11", + "name": "Carnival Tuesday" + }, + { + "date": "2020-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-07", + "name": "Independence Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-24", + "name": "Whit Monday" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-08-09", + "name": "Carnival Monday" + }, + { + "date": "2021-08-10", + "name": "Carnival Tuesday" + }, + { + "date": "2021-10-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "GF": { + "name": "Guyane", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-10", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GG": { + "name": "Guernsey", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-09", + "name": "Liberation Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-09", + "name": "Liberation Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-09", + "name": "Liberation Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-09", + "name": "Liberation Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GI": { + "name": "Gibraltar", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-12", + "name": "Commonwealth Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2018-05-01", + "name": "May Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-06-18", + "name": "Queen's Birthday" + }, + { + "date": "2018-08-27", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2018-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-11", + "name": "Commonwealth Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2019-05-01", + "name": "May Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-06-17", + "name": "Queen's Birthday" + }, + { + "date": "2019-08-26", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2019-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Commonwealth Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2020-05-01", + "name": "May Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-06-15", + "name": "Queen's Birthday" + }, + { + "date": "2020-08-31", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2020-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-08", + "name": "Commonwealth Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-28", + "name": "Workers Memorial Day" + }, + { + "date": "2021-05-03", + "name": "May Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-06-14", + "name": "Queen's Birthday" + }, + { + "date": "2021-08-30", + "name": "Late Summer Bank Holiday" + }, + { + "date": "2021-09-10", + "name": "Gibraltar National Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "GL": { + "name": "Kalaallit Nunaat", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "ukiortaaq" + }, + { + "date": "2018-01-06", + "name": "Åbenbaring" + }, + { + "date": "2018-03-29", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2018-03-30", + "name": "tallimanngornersuaq" + }, + { + "date": "2018-04-01", + "name": "poorskip-ullua" + }, + { + "date": "2018-04-02", + "name": "poorskip-aappaa" + }, + { + "date": "2018-04-27", + "name": "tussiarfissuaq" + }, + { + "date": "2018-05-10", + "name": "qilaliarfik" + }, + { + "date": "2018-05-20", + "name": "piinsip ullua" + }, + { + "date": "2018-05-21", + "name": "piinsip aappaa" + }, + { + "date": "2018-06-21", + "name": "ullortuneq" + }, + { + "date": "2018-12-24", + "name": "juulliaraq" + }, + { + "date": "2018-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2018-12-26", + "name": "juullip aappaa" + }, + { + "date": "2019-01-01", + "name": "ukiortaaq" + }, + { + "date": "2019-01-06", + "name": "Åbenbaring" + }, + { + "date": "2019-04-18", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2019-04-19", + "name": "tallimanngornersuaq" + }, + { + "date": "2019-04-21", + "name": "poorskip-ullua" + }, + { + "date": "2019-04-22", + "name": "poorskip-aappaa" + }, + { + "date": "2019-05-17", + "name": "tussiarfissuaq" + }, + { + "date": "2019-05-30", + "name": "qilaliarfik" + }, + { + "date": "2019-06-09", + "name": "piinsip ullua" + }, + { + "date": "2019-06-10", + "name": "piinsip aappaa" + }, + { + "date": "2019-06-21", + "name": "ullortuneq" + }, + { + "date": "2019-12-24", + "name": "juulliaraq" + }, + { + "date": "2019-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2019-12-26", + "name": "juullip aappaa" + }, + { + "date": "2020-01-01", + "name": "ukiortaaq" + }, + { + "date": "2020-01-06", + "name": "Åbenbaring" + }, + { + "date": "2020-04-09", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2020-04-10", + "name": "tallimanngornersuaq" + }, + { + "date": "2020-04-12", + "name": "poorskip-ullua" + }, + { + "date": "2020-04-13", + "name": "poorskip-aappaa" + }, + { + "date": "2020-05-08", + "name": "tussiarfissuaq" + }, + { + "date": "2020-05-21", + "name": "qilaliarfik" + }, + { + "date": "2020-05-31", + "name": "piinsip ullua" + }, + { + "date": "2020-06-01", + "name": "piinsip aappaa" + }, + { + "date": "2020-06-21", + "name": "ullortuneq" + }, + { + "date": "2020-12-24", + "name": "juulliaraq" + }, + { + "date": "2020-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2020-12-26", + "name": "juullip aappaa" + }, + { + "date": "2021-01-01", + "name": "ukiortaaq" + }, + { + "date": "2021-01-06", + "name": "Åbenbaring" + }, + { + "date": "2021-04-01", + "name": "sisamanngortoq illernartoq" + }, + { + "date": "2021-04-02", + "name": "tallimanngornersuaq" + }, + { + "date": "2021-04-04", + "name": "poorskip-ullua" + }, + { + "date": "2021-04-05", + "name": "poorskip-aappaa" + }, + { + "date": "2021-04-30", + "name": "tussiarfissuaq" + }, + { + "date": "2021-05-13", + "name": "qilaliarfik" + }, + { + "date": "2021-05-23", + "name": "piinsip ullua" + }, + { + "date": "2021-05-24", + "name": "piinsip aappaa" + }, + { + "date": "2021-06-21", + "name": "ullortuneq" + }, + { + "date": "2021-12-24", + "name": "juulliaraq" + }, + { + "date": "2021-12-25", + "name": "juullerujussuaq" + }, + { + "date": "2021-12-26", + "name": "juullip aappaa" + } + ] + }, + "GP": { + "name": "Guadeloupe", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-05-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "GQ": { + "name": "República de Guinea Ecuatorial", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-31", + "name": "Corpus Christi" + }, + { + "date": "2018-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2018-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2018-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2018-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2019-06-20", + "name": "Corpus Christi" + }, + { + "date": "2019-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2019-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2019-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-09", + "name": "La inmaculada concepción (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2020-06-11", + "name": "Corpus Christi" + }, + { + "date": "2020-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2020-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2020-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-03", + "name": "Corpus Christi" + }, + { + "date": "2021-06-05", + "name": "Natalicio de Teodoro Obiang" + }, + { + "date": "2021-08-03", + "name": "Día del Golpe de Libertad" + }, + { + "date": "2021-08-15", + "name": "Día de la Constitución" + }, + { + "date": "2021-08-16", + "name": "Día de la Constitución (día sustituto)" + }, + { + "date": "2021-10-12", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "GR": { + "name": "Ελλάδα", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2018-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2018-02-19", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2018-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2018-04-06", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2018-04-08", + "name": "Πάσχα" + }, + { + "date": "2018-04-09", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2018-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2018-05-27", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2018-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2018-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2018-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2018-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2019-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2019-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2019-03-11", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2019-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2019-04-26", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2019-04-28", + "name": "Πάσχα" + }, + { + "date": "2019-04-29", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2019-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2019-06-16", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2019-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2019-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2019-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2019-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2020-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2020-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2020-03-02", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2020-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2020-04-17", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2020-04-19", + "name": "Πάσχα" + }, + { + "date": "2020-04-20", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2020-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2020-06-07", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2020-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2020-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2020-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2020-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + }, + { + "date": "2021-01-01", + "name": "Πρωτοχρονιά" + }, + { + "date": "2021-01-06", + "name": "Θεοφάνεια" + }, + { + "date": "2021-03-15", + "name": "Καθαρά Δευτέρα" + }, + { + "date": "2021-03-25", + "name": "Ευαγγελισμός, Εθνική Εορτή" + }, + { + "date": "2021-04-30", + "name": "Μεγάλη Παρασκευή" + }, + { + "date": "2021-05-01", + "name": "Εργατική Πρωτομαγιά" + }, + { + "date": "2021-05-02", + "name": "Πάσχα" + }, + { + "date": "2021-05-03", + "name": "Δευτέρα του Πάσχα" + }, + { + "date": "2021-06-20", + "name": "Αγίου Πνεύματος" + }, + { + "date": "2021-08-15", + "name": "Κοίμηση της Θεοτόκου" + }, + { + "date": "2021-10-28", + "name": "Επέτειος του Όχι" + }, + { + "date": "2021-12-25", + "name": "Χριστούγεννα" + }, + { + "date": "2021-12-26", + "name": "Δεύτερη μέρα των Χριστουγέννων" + } + ] + }, + "GT": { + "name": "Guatemala", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-03-31", + "name": "Sabado Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-30", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-20", + "name": "Sabado Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-30", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Sabado Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-29", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-03", + "name": "Sabado Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-02", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-20", + "name": "Día de la Revolución" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "GU": { + "name": "Guam", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-07-21", + "name": "Liberation Day" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-02", + "name": "All Souls' Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-07-21", + "name": "Liberation Day" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-02", + "name": "All Souls' Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-07-21", + "name": "Liberation Day" + }, + { + "date": "2020-09-07", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-02", + "name": "All Souls' Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-03-05", + "name": "Guam Discovery Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-07-21", + "name": "Liberation Day" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-02", + "name": "All Souls' Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-08", + "name": "Lady of Camarin Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + } + ] + }, + "GY": { + "name": "Guyana", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-23", + "name": "Republic Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-05", + "name": "Arrival Day" + }, + { + "date": "2018-05-26", + "name": "Independence Day" + }, + { + "date": "2018-07-02", + "name": "CARICOM Day" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-21", + "name": "Eid Ul Adha" + }, + { + "date": "2018-11-20", + "name": "Youman Nabi" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-02-23", + "name": "Republic Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-05", + "name": "Arrival Day" + }, + { + "date": "2019-05-26", + "name": "Independence Day" + }, + { + "date": "2019-07-01", + "name": "CARICOM Day" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-11", + "name": "Eid Ul Adha" + }, + { + "date": "2019-11-09", + "name": "Youman Nabi" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-23", + "name": "Republic Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-05", + "name": "Arrival Day" + }, + { + "date": "2020-05-26", + "name": "Independence Day" + }, + { + "date": "2020-07-06", + "name": "CARICOM Day" + }, + { + "date": "2020-07-31", + "name": "Eid Ul Adha" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-10-29", + "name": "Youman Nabi" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-23", + "name": "Republic Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-05", + "name": "Arrival Day" + }, + { + "date": "2021-05-26", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "CARICOM Day" + }, + { + "date": "2021-07-20", + "name": "Eid Ul Adha" + }, + { + "date": "2021-08-01", + "name": "Emancipation Day" + }, + { + "date": "2021-10-18", + "name": "Youman Nabi" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "HN": { + "name": "Honduras", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-14", + "name": "Día de las Américas" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-03", + "name": "Día del Soldado" + }, + { + "date": "2018-10-12", + "name": "Día de la Raza" + }, + { + "date": "2018-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-14", + "name": "Día de las Américas" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-03", + "name": "Día del Soldado" + }, + { + "date": "2019-10-12", + "name": "Día de la Raza" + }, + { + "date": "2019-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-14", + "name": "Día de las Américas" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-03", + "name": "Día del Soldado" + }, + { + "date": "2020-10-12", + "name": "Día de la Raza" + }, + { + "date": "2020-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-14", + "name": "Día de las Américas" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-03", + "name": "Día del Soldado" + }, + { + "date": "2021-10-12", + "name": "Día de la Raza" + }, + { + "date": "2021-10-21", + "name": "Día de las Fuerzas Armadas" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "HR": { + "name": "Hrvatska", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nova godina" + }, + { + "date": "2018-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2018-02-13", + "name": "Pokladni utorak" + }, + { + "date": "2018-04-02", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2018-05-01", + "name": "Praznik rada" + }, + { + "date": "2018-05-31", + "name": "Tijelovo" + }, + { + "date": "2018-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2018-06-25", + "name": "Dan državnosti" + }, + { + "date": "2018-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2018-08-15", + "name": "Velika Gospa" + }, + { + "date": "2018-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2018-11-01", + "name": "Svi sveti" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2019-01-01", + "name": "Nova godina" + }, + { + "date": "2019-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2019-03-05", + "name": "Pokladni utorak" + }, + { + "date": "2019-04-22", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2019-05-01", + "name": "Praznik rada" + }, + { + "date": "2019-06-20", + "name": "Tijelovo" + }, + { + "date": "2019-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2019-06-25", + "name": "Dan državnosti" + }, + { + "date": "2019-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2019-08-15", + "name": "Velika Gospa" + }, + { + "date": "2019-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2019-11-01", + "name": "Svi sveti" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2020-01-01", + "name": "Nova godina" + }, + { + "date": "2020-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2020-02-25", + "name": "Pokladni utorak" + }, + { + "date": "2020-04-13", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2020-05-01", + "name": "Praznik rada" + }, + { + "date": "2020-06-11", + "name": "Tijelovo" + }, + { + "date": "2020-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2020-06-25", + "name": "Dan državnosti" + }, + { + "date": "2020-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2020-08-15", + "name": "Velika Gospa" + }, + { + "date": "2020-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2020-11-01", + "name": "Svi sveti" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Svetog Stjepana" + }, + { + "date": "2021-01-01", + "name": "Nova godina" + }, + { + "date": "2021-01-06", + "name": "Bogojavljenje, Sveta tri kralja" + }, + { + "date": "2021-02-16", + "name": "Pokladni utorak" + }, + { + "date": "2021-04-05", + "name": "Drugi dan Uskrsa" + }, + { + "date": "2021-05-01", + "name": "Praznik rada" + }, + { + "date": "2021-06-03", + "name": "Tijelovo" + }, + { + "date": "2021-06-22", + "name": "Dan antifašističke borbe" + }, + { + "date": "2021-06-25", + "name": "Dan državnosti" + }, + { + "date": "2021-08-05", + "name": "Dan pobjede i domovinske zahvalnosti" + }, + { + "date": "2021-08-15", + "name": "Velika Gospa" + }, + { + "date": "2021-10-08", + "name": "Dan neovisnosti" + }, + { + "date": "2021-11-01", + "name": "Svi sveti" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Svetog Stjepana" + } + ] + }, + "HT": { + "name": "Haïti", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2018-02-12", + "name": "Lundi Gras" + }, + { + "date": "2018-02-13", + "name": "Mardi Gras" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-01", + "name": "Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2018-05-31", + "name": "la Fête-Dieu" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-02", + "name": "Fête des morts" + }, + { + "date": "2018-11-18", + "name": "Vertières" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2019-03-04", + "name": "Lundi Gras" + }, + { + "date": "2019-03-05", + "name": "Mardi Gras" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-21", + "name": "Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-20", + "name": "la Fête-Dieu" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-02", + "name": "Fête des morts" + }, + { + "date": "2019-11-18", + "name": "Vertières" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2020-02-24", + "name": "Lundi Gras" + }, + { + "date": "2020-02-25", + "name": "Mardi Gras" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-12", + "name": "Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-11", + "name": "la Fête-Dieu" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-02", + "name": "Fête des morts" + }, + { + "date": "2020-11-18", + "name": "Vertières" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-01-02", + "name": "Jour des Aieux" + }, + { + "date": "2021-02-15", + "name": "Lundi Gras" + }, + { + "date": "2021-02-16", + "name": "Mardi Gras" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-04", + "name": "Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-18", + "name": "Jour du Drapeau et de l'Université" + }, + { + "date": "2021-06-03", + "name": "la Fête-Dieu" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-17", + "name": "Anniversaire de la mort de Dessalines" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-02", + "name": "Fête des morts" + }, + { + "date": "2021-11-18", + "name": "Vertières" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "HU": { + "name": "Magyarország", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Újév" + }, + { + "date": "2018-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2018-04-01", + "name": "Húsvétvasárnap" + }, + { + "date": "2018-04-02", + "name": "Húsvéthétfő" + }, + { + "date": "2018-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2018-05-20", + "name": "Pünkösdvasárnap" + }, + { + "date": "2018-05-21", + "name": "Pünkösdhétfő" + }, + { + "date": "2018-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2018-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2018-11-01", + "name": "Mindenszentek" + }, + { + "date": "2018-12-25", + "name": "Karácsony" + }, + { + "date": "2018-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2019-01-01", + "name": "Újév" + }, + { + "date": "2019-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2019-04-21", + "name": "Húsvétvasárnap" + }, + { + "date": "2019-04-22", + "name": "Húsvéthétfő" + }, + { + "date": "2019-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2019-06-09", + "name": "Pünkösdvasárnap" + }, + { + "date": "2019-06-10", + "name": "Pünkösdhétfő" + }, + { + "date": "2019-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2019-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2019-11-01", + "name": "Mindenszentek" + }, + { + "date": "2019-12-25", + "name": "Karácsony" + }, + { + "date": "2019-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2020-01-01", + "name": "Újév" + }, + { + "date": "2020-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2020-04-12", + "name": "Húsvétvasárnap" + }, + { + "date": "2020-04-13", + "name": "Húsvéthétfő" + }, + { + "date": "2020-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2020-05-31", + "name": "Pünkösdvasárnap" + }, + { + "date": "2020-06-01", + "name": "Pünkösdhétfő" + }, + { + "date": "2020-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2020-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2020-11-01", + "name": "Mindenszentek" + }, + { + "date": "2020-12-25", + "name": "Karácsony" + }, + { + "date": "2020-12-26", + "name": "Karácsony másnapja" + }, + { + "date": "2021-01-01", + "name": "Újév" + }, + { + "date": "2021-03-15", + "name": "Nemzeti ünnep" + }, + { + "date": "2021-04-04", + "name": "Húsvétvasárnap" + }, + { + "date": "2021-04-05", + "name": "Húsvéthétfő" + }, + { + "date": "2021-05-01", + "name": "A munka ünnepe" + }, + { + "date": "2021-05-23", + "name": "Pünkösdvasárnap" + }, + { + "date": "2021-05-24", + "name": "Pünkösdhétfő" + }, + { + "date": "2021-08-20", + "name": "Szent István ünnepe" + }, + { + "date": "2021-10-23", + "name": "Nemzeti ünnep" + }, + { + "date": "2021-11-01", + "name": "Mindenszentek" + }, + { + "date": "2021-12-25", + "name": "Karácsony" + }, + { + "date": "2021-12-26", + "name": "Karácsony másnapja" + } + ] + }, + "IE": { + "name": "Ireland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "May Day" + }, + { + "date": "2018-06-04", + "name": "First Monday in June" + }, + { + "date": "2018-08-06", + "name": "First Monday in August" + }, + { + "date": "2018-10-29", + "name": "October Bank Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "May Day" + }, + { + "date": "2019-06-03", + "name": "First Monday in June" + }, + { + "date": "2019-08-05", + "name": "First Monday in August" + }, + { + "date": "2019-10-28", + "name": "October Bank Holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "May Day" + }, + { + "date": "2020-06-01", + "name": "First Monday in June" + }, + { + "date": "2020-08-03", + "name": "First Monday in August" + }, + { + "date": "2020-10-26", + "name": "October Bank Holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "St. Stephen's Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-17", + "name": "St. Patrick’s Day" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "May Day" + }, + { + "date": "2021-06-07", + "name": "First Monday in June" + }, + { + "date": "2021-08-02", + "name": "First Monday in August" + }, + { + "date": "2021-10-25", + "name": "October Bank Holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "St. Stephen's Day" + } + ] + }, + "IM": { + "name": "Isle of Man", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-06-08", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2018-07-05", + "name": "Tynwald Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-06-14", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2019-07-05", + "name": "Tynwald Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-06-12", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2020-07-05", + "name": "Tynwald Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-06-11", + "name": "Tourist Trophy, Senior Race Day" + }, + { + "date": "2021-07-05", + "name": "Tynwald Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "IS": { + "name": "Ísland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nýársdagur" + }, + { + "date": "2018-03-29", + "name": "Skírdagur" + }, + { + "date": "2018-03-30", + "name": "Föstudagurinn langi" + }, + { + "date": "2018-04-01", + "name": "Páskadagur" + }, + { + "date": "2018-04-02", + "name": "Annar í páskum" + }, + { + "date": "2018-04-19", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2018-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2018-05-10", + "name": "Uppstigningardagur" + }, + { + "date": "2018-05-20", + "name": "Hvítasunnudagur" + }, + { + "date": "2018-05-21", + "name": "Annar í hvítasunnu" + }, + { + "date": "2018-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2018-08-06", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2018-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2018-12-25", + "name": "Jóladagur" + }, + { + "date": "2018-12-26", + "name": "Annar í jólum" + }, + { + "date": "2018-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2019-01-01", + "name": "Nýársdagur" + }, + { + "date": "2019-04-18", + "name": "Skírdagur" + }, + { + "date": "2019-04-18", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2019-04-19", + "name": "Föstudagurinn langi" + }, + { + "date": "2019-04-21", + "name": "Páskadagur" + }, + { + "date": "2019-04-22", + "name": "Annar í páskum" + }, + { + "date": "2019-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2019-05-30", + "name": "Uppstigningardagur" + }, + { + "date": "2019-06-09", + "name": "Hvítasunnudagur" + }, + { + "date": "2019-06-10", + "name": "Annar í hvítasunnu" + }, + { + "date": "2019-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2019-08-05", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2019-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2019-12-25", + "name": "Jóladagur" + }, + { + "date": "2019-12-26", + "name": "Annar í jólum" + }, + { + "date": "2019-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2020-01-01", + "name": "Nýársdagur" + }, + { + "date": "2020-04-09", + "name": "Skírdagur" + }, + { + "date": "2020-04-10", + "name": "Föstudagurinn langi" + }, + { + "date": "2020-04-12", + "name": "Páskadagur" + }, + { + "date": "2020-04-13", + "name": "Annar í páskum" + }, + { + "date": "2020-04-23", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2020-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2020-05-21", + "name": "Uppstigningardagur" + }, + { + "date": "2020-05-31", + "name": "Hvítasunnudagur" + }, + { + "date": "2020-06-01", + "name": "Annar í hvítasunnu" + }, + { + "date": "2020-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2020-08-03", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2020-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2020-12-25", + "name": "Jóladagur" + }, + { + "date": "2020-12-26", + "name": "Annar í jólum" + }, + { + "date": "2020-12-31", + "name": "Gamlársdagur" + }, + { + "date": "2021-01-01", + "name": "Nýársdagur" + }, + { + "date": "2021-04-01", + "name": "Skírdagur" + }, + { + "date": "2021-04-02", + "name": "Föstudagurinn langi" + }, + { + "date": "2021-04-04", + "name": "Páskadagur" + }, + { + "date": "2021-04-05", + "name": "Annar í páskum" + }, + { + "date": "2021-04-22", + "name": "Sumardagurinn fyrsti" + }, + { + "date": "2021-05-01", + "name": "Hátíðisdagur Verkamanna" + }, + { + "date": "2021-05-13", + "name": "Uppstigningardagur" + }, + { + "date": "2021-05-23", + "name": "Hvítasunnudagur" + }, + { + "date": "2021-05-24", + "name": "Annar í hvítasunnu" + }, + { + "date": "2021-06-17", + "name": "Íslenski þjóðhátíðardagurinn" + }, + { + "date": "2021-08-02", + "name": "Frídagur verslunarmanna" + }, + { + "date": "2021-12-24", + "name": "Aðfangadagur" + }, + { + "date": "2021-12-25", + "name": "Jóladagur" + }, + { + "date": "2021-12-26", + "name": "Annar í jólum" + }, + { + "date": "2021-12-31", + "name": "Gamlársdagur" + } + ] + }, + "IT": { + "name": "Italia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Capodanno" + }, + { + "date": "2018-01-06", + "name": "Befana" + }, + { + "date": "2018-04-01", + "name": "Domenica di Pasqua" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2018-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2018-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2018-08-15", + "name": "Ferragosto" + }, + { + "date": "2018-11-01", + "name": "Ognissanti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Capodanno" + }, + { + "date": "2019-01-06", + "name": "Befana" + }, + { + "date": "2019-04-21", + "name": "Domenica di Pasqua" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2019-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2019-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2019-08-15", + "name": "Ferragosto" + }, + { + "date": "2019-11-01", + "name": "Ognissanti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Capodanno" + }, + { + "date": "2020-01-06", + "name": "Befana" + }, + { + "date": "2020-04-12", + "name": "Domenica di Pasqua" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2020-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2020-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2020-08-15", + "name": "Ferragosto" + }, + { + "date": "2020-11-01", + "name": "Ognissanti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Capodanno" + }, + { + "date": "2021-01-06", + "name": "Befana" + }, + { + "date": "2021-04-04", + "name": "Domenica di Pasqua" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-04-25", + "name": "Anniversario della Liberazione" + }, + { + "date": "2021-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2021-06-02", + "name": "Festa della Repubblica" + }, + { + "date": "2021-08-15", + "name": "Ferragosto" + }, + { + "date": "2021-11-01", + "name": "Ognissanti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "JE": { + "name": "Jersey", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-07", + "name": "Early May bank holiday" + }, + { + "date": "2018-05-09", + "name": "Liberation Day" + }, + { + "date": "2018-05-28", + "name": "Spring bank holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-06", + "name": "Early May bank holiday" + }, + { + "date": "2019-05-09", + "name": "Liberation Day" + }, + { + "date": "2019-05-27", + "name": "Spring bank holiday" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-04", + "name": "Early May bank holiday" + }, + { + "date": "2020-05-09", + "name": "Liberation Day" + }, + { + "date": "2020-05-25", + "name": "Spring bank holiday" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-03", + "name": "Early May bank holiday" + }, + { + "date": "2021-05-09", + "name": "Liberation Day" + }, + { + "date": "2021-05-31", + "name": "Spring bank holiday" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-12-28", + "name": "Christmas Day (substitute day)" + } + ] + }, + "JM": { + "name": "Jamaica", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-02-14", + "name": "Ash Wednesday" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-23", + "name": "Labour Day" + }, + { + "date": "2018-08-01", + "name": "Emancipation Day" + }, + { + "date": "2018-08-06", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "National Heroes Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-06", + "name": "Ash Wednesday" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-23", + "name": "Labour Day" + }, + { + "date": "2019-08-01", + "name": "Emancipation Day" + }, + { + "date": "2019-08-06", + "name": "Independence Day" + }, + { + "date": "2019-10-21", + "name": "National Heroes Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-02-26", + "name": "Ash Wednesday" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-25", + "name": "Labour Day" + }, + { + "date": "2020-08-01", + "name": "Emancipation Day" + }, + { + "date": "2020-08-06", + "name": "Independence Day" + }, + { + "date": "2020-10-19", + "name": "National Heroes Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-02-17", + "name": "Ash Wednesday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-24", + "name": "Labour Day" + }, + { + "date": "2021-08-02", + "name": "Emancipation Day" + }, + { + "date": "2021-08-06", + "name": "Independence Day" + }, + { + "date": "2021-10-18", + "name": "National Heroes Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "JP": { + "name": "日本", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "元日" + }, + { + "date": "2018-01-02", + "name": "銀行休業日" + }, + { + "date": "2018-01-03", + "name": "銀行休業日" + }, + { + "date": "2018-01-08", + "name": "成人の日" + }, + { + "date": "2018-02-11", + "name": "建国記念の日" + }, + { + "date": "2018-02-12", + "name": "建国記念の日 (代替日)" + }, + { + "date": "2018-03-21", + "name": "春分の日" + }, + { + "date": "2018-04-29", + "name": "昭和の日" + }, + { + "date": "2018-04-30", + "name": "昭和の日 (代替日)" + }, + { + "date": "2018-05-03", + "name": "憲法記念日" + }, + { + "date": "2018-05-04", + "name": "みどりの日" + }, + { + "date": "2018-05-05", + "name": "こどもの日" + }, + { + "date": "2018-07-16", + "name": "海の日" + }, + { + "date": "2018-08-11", + "name": "山の日" + }, + { + "date": "2018-09-17", + "name": "敬老の日" + }, + { + "date": "2018-09-23", + "name": "秋分の日" + }, + { + "date": "2018-09-24", + "name": "秋分の日 (代替日)" + }, + { + "date": "2018-10-08", + "name": "体育の日" + }, + { + "date": "2018-11-03", + "name": "文化の日" + }, + { + "date": "2018-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2018-12-23", + "name": "天皇誕生日" + }, + { + "date": "2018-12-24", + "name": "天皇誕生日 (代替日)" + }, + { + "date": "2018-12-31", + "name": "大晦日" + }, + { + "date": "2019-01-01", + "name": "元日" + }, + { + "date": "2019-01-02", + "name": "銀行休業日" + }, + { + "date": "2019-01-03", + "name": "銀行休業日" + }, + { + "date": "2019-01-14", + "name": "成人の日" + }, + { + "date": "2019-02-11", + "name": "建国記念の日" + }, + { + "date": "2019-03-21", + "name": "春分の日" + }, + { + "date": "2019-04-29", + "name": "昭和の日" + }, + { + "date": "2019-05-03", + "name": "憲法記念日" + }, + { + "date": "2019-05-04", + "name": "みどりの日" + }, + { + "date": "2019-05-05", + "name": "こどもの日" + }, + { + "date": "2019-05-06", + "name": "こどもの日 (代替日)" + }, + { + "date": "2019-07-15", + "name": "海の日" + }, + { + "date": "2019-08-11", + "name": "山の日" + }, + { + "date": "2019-08-12", + "name": "山の日 (代替日)" + }, + { + "date": "2019-09-16", + "name": "敬老の日" + }, + { + "date": "2019-09-23", + "name": "秋分の日" + }, + { + "date": "2019-10-14", + "name": "体育の日" + }, + { + "date": "2019-11-03", + "name": "文化の日" + }, + { + "date": "2019-11-04", + "name": "文化の日 (代替日)" + }, + { + "date": "2019-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2019-12-23", + "name": "天皇誕生日" + }, + { + "date": "2019-12-31", + "name": "大晦日" + }, + { + "date": "2020-01-01", + "name": "元日" + }, + { + "date": "2020-01-02", + "name": "銀行休業日" + }, + { + "date": "2020-01-03", + "name": "銀行休業日" + }, + { + "date": "2020-01-13", + "name": "成人の日" + }, + { + "date": "2020-02-11", + "name": "建国記念の日" + }, + { + "date": "2020-03-20", + "name": "春分の日" + }, + { + "date": "2020-04-29", + "name": "昭和の日" + }, + { + "date": "2020-05-03", + "name": "憲法記念日" + }, + { + "date": "2020-05-04", + "name": "みどりの日" + }, + { + "date": "2020-05-05", + "name": "こどもの日" + }, + { + "date": "2020-05-06", + "name": "憲法記念日 (代替日)" + }, + { + "date": "2020-07-20", + "name": "海の日" + }, + { + "date": "2020-08-11", + "name": "山の日" + }, + { + "date": "2020-09-21", + "name": "敬老の日" + }, + { + "date": "2020-09-22", + "name": "秋分の日" + }, + { + "date": "2020-10-12", + "name": "体育の日" + }, + { + "date": "2020-11-03", + "name": "文化の日" + }, + { + "date": "2020-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2020-12-23", + "name": "天皇誕生日" + }, + { + "date": "2020-12-31", + "name": "大晦日" + }, + { + "date": "2021-01-01", + "name": "元日" + }, + { + "date": "2021-01-02", + "name": "銀行休業日" + }, + { + "date": "2021-01-03", + "name": "銀行休業日" + }, + { + "date": "2021-01-11", + "name": "成人の日" + }, + { + "date": "2021-02-11", + "name": "建国記念の日" + }, + { + "date": "2021-03-20", + "name": "春分の日" + }, + { + "date": "2021-04-29", + "name": "昭和の日" + }, + { + "date": "2021-05-03", + "name": "憲法記念日" + }, + { + "date": "2021-05-04", + "name": "みどりの日" + }, + { + "date": "2021-05-05", + "name": "こどもの日" + }, + { + "date": "2021-07-19", + "name": "海の日" + }, + { + "date": "2021-08-11", + "name": "山の日" + }, + { + "date": "2021-09-20", + "name": "敬老の日" + }, + { + "date": "2021-09-23", + "name": "秋分の日" + }, + { + "date": "2021-10-11", + "name": "体育の日" + }, + { + "date": "2021-11-03", + "name": "文化の日" + }, + { + "date": "2021-11-23", + "name": "勤労感謝の日" + }, + { + "date": "2021-12-23", + "name": "天皇誕生日" + }, + { + "date": "2021-12-31", + "name": "大晦日" + } + ] + }, + "KE": { + "name": "Kenia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2018-03-30", + "name": "Ijumaa Kuu" + }, + { + "date": "2018-04-01", + "name": "Pasaka" + }, + { + "date": "2018-04-02", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2018-06-15", + "name": "Idd-ul-Fitr" + }, + { + "date": "2018-08-21", + "name": "Idd-ul-Azha" + }, + { + "date": "2018-10-10", + "name": "Moi Day" + }, + { + "date": "2018-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2018-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2018-12-25", + "name": "Krismasi" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2019-04-19", + "name": "Ijumaa Kuu" + }, + { + "date": "2019-04-21", + "name": "Pasaka" + }, + { + "date": "2019-04-22", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2019-06-04", + "name": "Idd-ul-Fitr" + }, + { + "date": "2019-08-11", + "name": "Idd-ul-Azha" + }, + { + "date": "2019-10-10", + "name": "Moi Day" + }, + { + "date": "2019-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2019-10-21", + "name": "Siku ya Mashujaa (substitute day)" + }, + { + "date": "2019-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2019-12-25", + "name": "Krismasi" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2020-04-10", + "name": "Ijumaa Kuu" + }, + { + "date": "2020-04-12", + "name": "Pasaka" + }, + { + "date": "2020-04-13", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "Idd-ul-Fitr" + }, + { + "date": "2020-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2020-07-31", + "name": "Idd-ul-Azha" + }, + { + "date": "2020-10-10", + "name": "Moi Day" + }, + { + "date": "2020-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2020-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2020-12-25", + "name": "Krismasi" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "Mwaka mpya" + }, + { + "date": "2021-04-02", + "name": "Ijumaa Kuu" + }, + { + "date": "2021-04-04", + "name": "Pasaka" + }, + { + "date": "2021-04-05", + "name": "Jumatatu ya Pasaka" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Idd-ul-Fitr" + }, + { + "date": "2021-06-01", + "name": "Siku ya Madaraka" + }, + { + "date": "2021-07-20", + "name": "Idd-ul-Azha" + }, + { + "date": "2021-10-10", + "name": "Moi Day" + }, + { + "date": "2021-10-11", + "name": "Moi Day (substitute day)" + }, + { + "date": "2021-10-20", + "name": "Siku ya Mashujaa" + }, + { + "date": "2021-12-12", + "name": "Siku ya Jamhuri" + }, + { + "date": "2021-12-13", + "name": "Siku ya Jamhuri (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Krismasi" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "KR": { + "name": "대한민국", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "신정" + }, + { + "date": "2018-02-16", + "name": "설날" + }, + { + "date": "2018-03-01", + "name": "3·1절" + }, + { + "date": "2018-05-05", + "name": "어린이날" + }, + { + "date": "2018-05-22", + "name": "석가탄신일" + }, + { + "date": "2018-06-06", + "name": "현충일" + }, + { + "date": "2018-08-15", + "name": "광복절" + }, + { + "date": "2018-09-24", + "name": "추석" + }, + { + "date": "2018-10-03", + "name": "개천절" + }, + { + "date": "2018-10-09", + "name": "한글날" + }, + { + "date": "2018-12-25", + "name": "기독탄신일" + }, + { + "date": "2019-01-01", + "name": "신정" + }, + { + "date": "2019-02-05", + "name": "설날" + }, + { + "date": "2019-03-01", + "name": "3·1절" + }, + { + "date": "2019-05-05", + "name": "어린이날" + }, + { + "date": "2019-05-12", + "name": "석가탄신일" + }, + { + "date": "2019-06-06", + "name": "현충일" + }, + { + "date": "2019-08-15", + "name": "광복절" + }, + { + "date": "2019-09-13", + "name": "추석" + }, + { + "date": "2019-10-03", + "name": "개천절" + }, + { + "date": "2019-10-09", + "name": "한글날" + }, + { + "date": "2019-12-25", + "name": "기독탄신일" + }, + { + "date": "2020-01-01", + "name": "신정" + }, + { + "date": "2020-01-25", + "name": "설날" + }, + { + "date": "2020-03-01", + "name": "3·1절" + }, + { + "date": "2020-04-30", + "name": "석가탄신일" + }, + { + "date": "2020-05-05", + "name": "어린이날" + }, + { + "date": "2020-06-06", + "name": "현충일" + }, + { + "date": "2020-08-15", + "name": "광복절" + }, + { + "date": "2020-10-01", + "name": "추석" + }, + { + "date": "2020-10-03", + "name": "개천절" + }, + { + "date": "2020-10-09", + "name": "한글날" + }, + { + "date": "2020-12-25", + "name": "기독탄신일" + }, + { + "date": "2021-01-01", + "name": "신정" + }, + { + "date": "2021-02-12", + "name": "설날" + }, + { + "date": "2021-03-01", + "name": "3·1절" + }, + { + "date": "2021-05-05", + "name": "어린이날" + }, + { + "date": "2021-05-19", + "name": "석가탄신일" + }, + { + "date": "2021-06-06", + "name": "현충일" + }, + { + "date": "2021-08-15", + "name": "광복절" + }, + { + "date": "2021-09-21", + "name": "추석" + }, + { + "date": "2021-10-03", + "name": "개천절" + }, + { + "date": "2021-10-09", + "name": "한글날" + }, + { + "date": "2021-12-25", + "name": "기독탄신일" + } + ] + }, + "LI": { + "name": "Lichtenstein", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Neujahr" + }, + { + "date": "2018-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2018-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2018-02-13", + "name": "Faschingsdienstag" + }, + { + "date": "2018-03-30", + "name": "Karfreitag" + }, + { + "date": "2018-04-02", + "name": "Ostermontag" + }, + { + "date": "2018-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2018-05-10", + "name": "Auffahrt" + }, + { + "date": "2018-05-11", + "name": "Feiertagsbrücke" + }, + { + "date": "2018-05-21", + "name": "Pfingstmontag" + }, + { + "date": "2018-05-31", + "name": "Fronleichnam" + }, + { + "date": "2018-06-01", + "name": "Feiertagsbrücke" + }, + { + "date": "2018-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2018-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2018-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2018-12-25", + "name": "Weihnachten" + }, + { + "date": "2018-12-26", + "name": "Stephanstag" + }, + { + "date": "2018-12-31", + "name": "Silvester" + }, + { + "date": "2019-01-01", + "name": "Neujahr" + }, + { + "date": "2019-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2019-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2019-03-05", + "name": "Faschingsdienstag" + }, + { + "date": "2019-04-19", + "name": "Karfreitag" + }, + { + "date": "2019-04-22", + "name": "Ostermontag" + }, + { + "date": "2019-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2019-05-30", + "name": "Auffahrt" + }, + { + "date": "2019-05-31", + "name": "Feiertagsbrücke" + }, + { + "date": "2019-06-10", + "name": "Pfingstmontag" + }, + { + "date": "2019-06-20", + "name": "Fronleichnam" + }, + { + "date": "2019-06-21", + "name": "Feiertagsbrücke" + }, + { + "date": "2019-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2019-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2019-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2019-12-25", + "name": "Weihnachten" + }, + { + "date": "2019-12-26", + "name": "Stephanstag" + }, + { + "date": "2019-12-31", + "name": "Silvester" + }, + { + "date": "2020-01-01", + "name": "Neujahr" + }, + { + "date": "2020-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2020-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2020-02-25", + "name": "Faschingsdienstag" + }, + { + "date": "2020-04-10", + "name": "Karfreitag" + }, + { + "date": "2020-04-13", + "name": "Ostermontag" + }, + { + "date": "2020-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2020-05-21", + "name": "Auffahrt" + }, + { + "date": "2020-05-22", + "name": "Feiertagsbrücke" + }, + { + "date": "2020-06-01", + "name": "Pfingstmontag" + }, + { + "date": "2020-06-11", + "name": "Fronleichnam" + }, + { + "date": "2020-06-12", + "name": "Feiertagsbrücke" + }, + { + "date": "2020-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2020-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2020-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2020-12-25", + "name": "Weihnachten" + }, + { + "date": "2020-12-26", + "name": "Stephanstag" + }, + { + "date": "2020-12-31", + "name": "Silvester" + }, + { + "date": "2021-01-01", + "name": "Neujahr" + }, + { + "date": "2021-01-02", + "name": "Berchtoldstag" + }, + { + "date": "2021-01-06", + "name": "Heilige Drei Könige" + }, + { + "date": "2021-02-16", + "name": "Faschingsdienstag" + }, + { + "date": "2021-04-02", + "name": "Karfreitag" + }, + { + "date": "2021-04-05", + "name": "Ostermontag" + }, + { + "date": "2021-05-01", + "name": "Tag der Arbeit" + }, + { + "date": "2021-05-13", + "name": "Auffahrt" + }, + { + "date": "2021-05-14", + "name": "Feiertagsbrücke" + }, + { + "date": "2021-05-24", + "name": "Pfingstmontag" + }, + { + "date": "2021-06-03", + "name": "Fronleichnam" + }, + { + "date": "2021-06-04", + "name": "Feiertagsbrücke" + }, + { + "date": "2021-09-08", + "name": "Mariä Geburt" + }, + { + "date": "2021-12-08", + "name": "Mariä Empfängnis" + }, + { + "date": "2021-12-24", + "name": "Heiliger Abend" + }, + { + "date": "2021-12-25", + "name": "Weihnachten" + }, + { + "date": "2021-12-26", + "name": "Stephanstag" + }, + { + "date": "2021-12-31", + "name": "Silvester" + } + ] + }, + "LT": { + "name": "Lietuva", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Naujieji metai" + }, + { + "date": "2018-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2018-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2018-04-01", + "name": "Velykos" + }, + { + "date": "2018-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2018-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2018-07-06", + "name": "Valstybės diena" + }, + { + "date": "2018-08-15", + "name": "Žolinė" + }, + { + "date": "2018-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2018-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2018-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2018-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2019-01-01", + "name": "Naujieji metai" + }, + { + "date": "2019-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2019-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2019-04-21", + "name": "Velykos" + }, + { + "date": "2019-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2019-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2019-07-06", + "name": "Valstybės diena" + }, + { + "date": "2019-08-15", + "name": "Žolinė" + }, + { + "date": "2019-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2019-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2019-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2019-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2020-01-01", + "name": "Naujieji metai" + }, + { + "date": "2020-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2020-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2020-04-12", + "name": "Velykos" + }, + { + "date": "2020-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2020-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2020-07-06", + "name": "Valstybės diena" + }, + { + "date": "2020-08-15", + "name": "Žolinė" + }, + { + "date": "2020-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2020-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2020-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2020-12-26", + "name": "2. Kalėdų diena" + }, + { + "date": "2021-01-01", + "name": "Naujieji metai" + }, + { + "date": "2021-02-16", + "name": "Lietuvos valstybės atkūrimo diena" + }, + { + "date": "2021-03-11", + "name": "Lietuvos nepriklausomybės atkūrimo diena" + }, + { + "date": "2021-04-04", + "name": "Velykos" + }, + { + "date": "2021-05-01", + "name": "Tarptautinė darbo diena" + }, + { + "date": "2021-06-24", + "name": "Joninės, Rasos" + }, + { + "date": "2021-07-06", + "name": "Valstybės diena" + }, + { + "date": "2021-08-15", + "name": "Žolinė" + }, + { + "date": "2021-11-01", + "name": "Visų šventųjų diena" + }, + { + "date": "2021-12-24", + "name": "Šv. Kūčios" + }, + { + "date": "2021-12-25", + "name": "Šv. Kalėdos" + }, + { + "date": "2021-12-26", + "name": "2. Kalėdų diena" + } + ] + }, + "LU": { + "name": "Luxembourg", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "1er mai" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "1er mai" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "1er mai" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "1er mai" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-23", + "name": "L’anniversaire du Grand-Duc" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "LV": { + "name": "Latvija", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2018-03-30", + "name": "Lielā Piektdiena" + }, + { + "date": "2018-04-01", + "name": "Lieldienas" + }, + { + "date": "2018-04-02", + "name": "Otrās Lieldienas" + }, + { + "date": "2018-05-01", + "name": "Darba svētki" + }, + { + "date": "2018-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2018-06-23", + "name": "Līgo Diena" + }, + { + "date": "2018-06-24", + "name": "Jāņi" + }, + { + "date": "2018-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2018-11-19", + "name": "Latvijas Republikas proklamēšanas diena (aizstājējs diena)" + }, + { + "date": "2018-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2018-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2018-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2018-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2019-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2019-04-19", + "name": "Lielā Piektdiena" + }, + { + "date": "2019-04-21", + "name": "Lieldienas" + }, + { + "date": "2019-04-22", + "name": "Otrās Lieldienas" + }, + { + "date": "2019-05-01", + "name": "Darba svētki" + }, + { + "date": "2019-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2019-05-06", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena (aizstājējs diena)" + }, + { + "date": "2019-06-23", + "name": "Līgo Diena" + }, + { + "date": "2019-06-24", + "name": "Jāņi" + }, + { + "date": "2019-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2019-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2019-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2019-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2019-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2020-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2020-04-10", + "name": "Lielā Piektdiena" + }, + { + "date": "2020-04-12", + "name": "Lieldienas" + }, + { + "date": "2020-04-13", + "name": "Otrās Lieldienas" + }, + { + "date": "2020-05-01", + "name": "Darba svētki" + }, + { + "date": "2020-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2020-06-23", + "name": "Līgo Diena" + }, + { + "date": "2020-06-24", + "name": "Jāņi" + }, + { + "date": "2020-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2020-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2020-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2020-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2020-12-31", + "name": "Vecgada vakars" + }, + { + "date": "2021-01-01", + "name": "Jaunais Gads" + }, + { + "date": "2021-04-02", + "name": "Lielā Piektdiena" + }, + { + "date": "2021-04-04", + "name": "Lieldienas" + }, + { + "date": "2021-04-05", + "name": "Otrās Lieldienas" + }, + { + "date": "2021-05-01", + "name": "Darba svētki" + }, + { + "date": "2021-05-04", + "name": "Latvijas Republikas Neatkarības atjaunošanas diena" + }, + { + "date": "2021-06-23", + "name": "Līgo Diena" + }, + { + "date": "2021-06-24", + "name": "Jāņi" + }, + { + "date": "2021-11-18", + "name": "Latvijas Republikas proklamēšanas diena" + }, + { + "date": "2021-12-24", + "name": "Ziemassvētku vakars" + }, + { + "date": "2021-12-25", + "name": "Ziemassvētki" + }, + { + "date": "2021-12-26", + "name": "Otrie Ziemassvētki" + }, + { + "date": "2021-12-31", + "name": "Vecgada vakars" + } + ] + }, + "MC": { + "name": "Monaco", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "1er mai" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-31", + "name": "la Fête-Dieu" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2018-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2019-01-28", + "name": "Sainte Dévote (jour substitut)" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "1er mai" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-20", + "name": "la Fête-Dieu" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2019-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "1er mai" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-11", + "name": "la Fête-Dieu" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2020-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-27", + "name": "Sainte Dévote" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "1er mai" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-03", + "name": "la Fête-Dieu" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-19", + "name": "La Fête du Prince" + }, + { + "date": "2021-12-08", + "name": "Immaculée Conception" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "MD": { + "name": "Republica Moldova", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Anul nou" + }, + { + "date": "2018-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2018-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2018-04-08", + "name": "Paștele" + }, + { + "date": "2018-04-09", + "name": "Două zi de Pasti" + }, + { + "date": "2018-04-16", + "name": "Paştele Blăjinilor" + }, + { + "date": "2018-05-01", + "name": "Ziua muncii" + }, + { + "date": "2018-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2018-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2018-08-31", + "name": "Limba noastră" + }, + { + "date": "2018-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2019-01-01", + "name": "Anul nou" + }, + { + "date": "2019-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2019-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2019-04-28", + "name": "Paștele" + }, + { + "date": "2019-04-29", + "name": "Două zi de Pasti" + }, + { + "date": "2019-05-01", + "name": "Ziua muncii" + }, + { + "date": "2019-05-06", + "name": "Paştele Blăjinilor" + }, + { + "date": "2019-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2019-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2019-08-31", + "name": "Limba noastră" + }, + { + "date": "2019-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2020-01-01", + "name": "Anul nou" + }, + { + "date": "2020-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2020-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2020-04-19", + "name": "Paștele" + }, + { + "date": "2020-04-20", + "name": "Două zi de Pasti" + }, + { + "date": "2020-04-27", + "name": "Paştele Blăjinilor" + }, + { + "date": "2020-05-01", + "name": "Ziua muncii" + }, + { + "date": "2020-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2020-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2020-08-31", + "name": "Limba noastră" + }, + { + "date": "2020-12-25", + "name": "Craciun pe stil Nou" + }, + { + "date": "2021-01-01", + "name": "Anul nou" + }, + { + "date": "2021-01-07", + "name": "Craciun pe Rit Vechi" + }, + { + "date": "2021-03-08", + "name": "Ziua Internationala a Femeii" + }, + { + "date": "2021-05-01", + "name": "Ziua muncii" + }, + { + "date": "2021-05-02", + "name": "Paștele" + }, + { + "date": "2021-05-03", + "name": "Două zi de Pasti" + }, + { + "date": "2021-05-09", + "name": "Ziua Victoriei" + }, + { + "date": "2021-05-10", + "name": "Paştele Blăjinilor" + }, + { + "date": "2021-08-27", + "name": "Ziua Independentei" + }, + { + "date": "2021-08-31", + "name": "Limba noastră" + }, + { + "date": "2021-12-25", + "name": "Craciun pe stil Nou" + } + ] + }, + "ME": { + "name": "Crna Gora", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nova godina" + }, + { + "date": "2018-01-02", + "name": "Nova godina" + }, + { + "date": "2018-01-06", + "name": "Badnji dan" + }, + { + "date": "2018-01-07", + "name": "Božić" + }, + { + "date": "2018-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2018-03-30", + "name": "Veliki petak" + }, + { + "date": "2018-03-31", + "name": "Pesač" + }, + { + "date": "2018-04-01", + "name": "Uskrs" + }, + { + "date": "2018-04-02", + "name": "Uskrs" + }, + { + "date": "2018-04-06", + "name": "Veliki petak" + }, + { + "date": "2018-04-08", + "name": "Uskrs" + }, + { + "date": "2018-05-01", + "name": "Praznik rada" + }, + { + "date": "2018-05-02", + "name": "Praznik rada" + }, + { + "date": "2018-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2018-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2018-06-15", + "name": "Ramazanski bajram" + }, + { + "date": "2018-07-13", + "name": "Dan državnosti" + }, + { + "date": "2018-07-14", + "name": "Dan državnosti" + }, + { + "date": "2018-08-21", + "name": "Kurban-bajram" + }, + { + "date": "2018-09-19", + "name": "Jom Kipur" + }, + { + "date": "2018-11-01", + "name": "Svi sveti" + }, + { + "date": "2018-12-24", + "name": "Badnji dan" + }, + { + "date": "2018-12-25", + "name": "Božić" + }, + { + "date": "2018-12-26", + "name": "Božić" + }, + { + "date": "2019-01-01", + "name": "Nova godina" + }, + { + "date": "2019-01-02", + "name": "Nova godina" + }, + { + "date": "2019-01-06", + "name": "Badnji dan" + }, + { + "date": "2019-01-07", + "name": "Božić" + }, + { + "date": "2019-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2019-04-19", + "name": "Veliki petak" + }, + { + "date": "2019-04-20", + "name": "Pesač" + }, + { + "date": "2019-04-21", + "name": "Uskrs" + }, + { + "date": "2019-04-22", + "name": "Uskrs" + }, + { + "date": "2019-04-26", + "name": "Veliki petak" + }, + { + "date": "2019-04-28", + "name": "Uskrs" + }, + { + "date": "2019-05-01", + "name": "Praznik rada" + }, + { + "date": "2019-05-02", + "name": "Praznik rada" + }, + { + "date": "2019-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2019-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2019-06-04", + "name": "Ramazanski bajram" + }, + { + "date": "2019-07-13", + "name": "Dan državnosti" + }, + { + "date": "2019-07-14", + "name": "Dan državnosti" + }, + { + "date": "2019-07-15", + "name": "Dan državnosti (zamjena dan)" + }, + { + "date": "2019-08-11", + "name": "Kurban-bajram" + }, + { + "date": "2019-10-09", + "name": "Jom Kipur" + }, + { + "date": "2019-11-01", + "name": "Svi sveti" + }, + { + "date": "2019-12-24", + "name": "Badnji dan" + }, + { + "date": "2019-12-25", + "name": "Božić" + }, + { + "date": "2019-12-26", + "name": "Božić" + }, + { + "date": "2020-01-01", + "name": "Nova godina" + }, + { + "date": "2020-01-02", + "name": "Nova godina" + }, + { + "date": "2020-01-06", + "name": "Badnji dan" + }, + { + "date": "2020-01-07", + "name": "Božić" + }, + { + "date": "2020-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2020-04-09", + "name": "Pesač" + }, + { + "date": "2020-04-10", + "name": "Veliki petak" + }, + { + "date": "2020-04-12", + "name": "Uskrs" + }, + { + "date": "2020-04-13", + "name": "Uskrs" + }, + { + "date": "2020-04-17", + "name": "Veliki petak" + }, + { + "date": "2020-04-19", + "name": "Uskrs" + }, + { + "date": "2020-05-01", + "name": "Praznik rada" + }, + { + "date": "2020-05-02", + "name": "Praznik rada" + }, + { + "date": "2020-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2020-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2020-05-24", + "name": "Ramazanski bajram" + }, + { + "date": "2020-07-13", + "name": "Dan državnosti" + }, + { + "date": "2020-07-14", + "name": "Dan državnosti" + }, + { + "date": "2020-07-31", + "name": "Kurban-bajram" + }, + { + "date": "2020-09-28", + "name": "Jom Kipur" + }, + { + "date": "2020-11-01", + "name": "Svi sveti" + }, + { + "date": "2020-12-24", + "name": "Badnji dan" + }, + { + "date": "2020-12-25", + "name": "Božić" + }, + { + "date": "2020-12-26", + "name": "Božić" + }, + { + "date": "2021-01-01", + "name": "Nova godina" + }, + { + "date": "2021-01-02", + "name": "Nova godina" + }, + { + "date": "2021-01-06", + "name": "Badnji dan" + }, + { + "date": "2021-01-07", + "name": "Božić" + }, + { + "date": "2021-01-08", + "name": "Svetog Stjepana" + }, + { + "date": "2021-03-28", + "name": "Pesač" + }, + { + "date": "2021-04-02", + "name": "Veliki petak" + }, + { + "date": "2021-04-04", + "name": "Uskrs" + }, + { + "date": "2021-04-05", + "name": "Uskrs" + }, + { + "date": "2021-04-30", + "name": "Veliki petak" + }, + { + "date": "2021-05-01", + "name": "Praznik rada" + }, + { + "date": "2021-05-02", + "name": "Uskrs" + }, + { + "date": "2021-05-02", + "name": "Praznik rada" + }, + { + "date": "2021-05-03", + "name": "Praznik rada (zamjena dan)" + }, + { + "date": "2021-05-13", + "name": "Ramazanski bajram" + }, + { + "date": "2021-05-21", + "name": "Dan neovisnosti" + }, + { + "date": "2021-05-22", + "name": "Dan neovisnosti" + }, + { + "date": "2021-07-13", + "name": "Dan državnosti" + }, + { + "date": "2021-07-14", + "name": "Dan državnosti" + }, + { + "date": "2021-07-20", + "name": "Kurban-bajram" + }, + { + "date": "2021-09-16", + "name": "Jom Kipur" + }, + { + "date": "2021-11-01", + "name": "Svi sveti" + }, + { + "date": "2021-12-24", + "name": "Badnji dan" + }, + { + "date": "2021-12-25", + "name": "Božić" + }, + { + "date": "2021-12-26", + "name": "Božić" + } + ] + }, + "MG": { + "name": "Repoblikan'i Madagasikara", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Taom-baovao" + }, + { + "date": "2018-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2018-04-02", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2018-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2018-05-10", + "name": "Andro niakarana" + }, + { + "date": "2018-05-21", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2018-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2018-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2018-08-15", + "name": "Asompsiona" + }, + { + "date": "2018-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2018-12-25", + "name": "Krismasy" + }, + { + "date": "2019-01-01", + "name": "Taom-baovao" + }, + { + "date": "2019-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2019-04-22", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2019-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2019-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2019-05-30", + "name": "Andro niakarana" + }, + { + "date": "2019-06-10", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2019-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2019-08-15", + "name": "Asompsiona" + }, + { + "date": "2019-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2019-12-25", + "name": "Krismasy" + }, + { + "date": "2020-01-01", + "name": "Taom-baovao" + }, + { + "date": "2020-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2020-04-13", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2020-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2020-05-21", + "name": "Andro niakarana" + }, + { + "date": "2020-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2020-06-01", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2020-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2020-08-15", + "name": "Asompsiona" + }, + { + "date": "2020-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2020-12-25", + "name": "Krismasy" + }, + { + "date": "2021-01-01", + "name": "Taom-baovao" + }, + { + "date": "2021-03-29", + "name": "Martioran'ny tolona tamin'ny 1947" + }, + { + "date": "2021-04-05", + "name": "Alatsinain'ny Paska" + }, + { + "date": "2021-05-01", + "name": "Fetin'ny asa" + }, + { + "date": "2021-05-13", + "name": "Andro niakarana" + }, + { + "date": "2021-05-24", + "name": "Alatsinain'ny Pentekosta" + }, + { + "date": "2021-05-25", + "name": "Andron'i Afrika" + }, + { + "date": "2021-06-26", + "name": "Fetim-pirenena" + }, + { + "date": "2021-08-15", + "name": "Asompsiona" + }, + { + "date": "2021-11-01", + "name": "Fetin'ny olo-masina" + }, + { + "date": "2021-12-25", + "name": "Krismasy" + } + ] + }, + "MK": { + "name": "Република Македонија", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова Година" + }, + { + "date": "2018-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2018-01-08", + "name": "Прв ден Божик (заменет ден)" + }, + { + "date": "2018-04-08", + "name": "Прв ден Велигден" + }, + { + "date": "2018-04-09", + "name": "Втор ден Велигден" + }, + { + "date": "2018-05-01", + "name": "Ден на трудот" + }, + { + "date": "2018-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2018-06-15", + "name": "Рамазан Бајрам" + }, + { + "date": "2018-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2018-09-08", + "name": "Ден на независноста" + }, + { + "date": "2018-10-11", + "name": "Ден на востанието" + }, + { + "date": "2018-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2018-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2019-01-01", + "name": "Нова Година" + }, + { + "date": "2019-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2019-04-28", + "name": "Прв ден Велигден" + }, + { + "date": "2019-04-29", + "name": "Втор ден Велигден" + }, + { + "date": "2019-05-01", + "name": "Ден на трудот" + }, + { + "date": "2019-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2019-06-04", + "name": "Рамазан Бајрам" + }, + { + "date": "2019-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2019-09-08", + "name": "Ден на независноста" + }, + { + "date": "2019-09-09", + "name": "Ден на независноста (заменет ден)" + }, + { + "date": "2019-10-11", + "name": "Ден на востанието" + }, + { + "date": "2019-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2019-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2019-12-09", + "name": "Св. Климент Охридски (заменет ден)" + }, + { + "date": "2020-01-01", + "name": "Нова Година" + }, + { + "date": "2020-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2020-04-19", + "name": "Прв ден Велигден" + }, + { + "date": "2020-04-20", + "name": "Втор ден Велигден" + }, + { + "date": "2020-05-01", + "name": "Ден на трудот" + }, + { + "date": "2020-05-24", + "name": "Рамазан Бајрам" + }, + { + "date": "2020-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2020-05-25", + "name": "Рамазан Бајрам (заменет ден)" + }, + { + "date": "2020-05-25", + "name": "Св. Кирил и Методиј (заменет ден)" + }, + { + "date": "2020-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2020-08-03", + "name": "Ден на Републиката (заменет ден)" + }, + { + "date": "2020-09-08", + "name": "Ден на независноста" + }, + { + "date": "2020-10-11", + "name": "Ден на востанието" + }, + { + "date": "2020-10-12", + "name": "Ден на востанието (заменет ден)" + }, + { + "date": "2020-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2020-12-08", + "name": "Св. Климент Охридски" + }, + { + "date": "2021-01-01", + "name": "Нова Година" + }, + { + "date": "2021-01-07", + "name": "Прв ден Божик" + }, + { + "date": "2021-05-01", + "name": "Ден на трудот" + }, + { + "date": "2021-05-02", + "name": "Прв ден Велигден" + }, + { + "date": "2021-05-03", + "name": "Втор ден Велигден" + }, + { + "date": "2021-05-13", + "name": "Рамазан Бајрам" + }, + { + "date": "2021-05-24", + "name": "Св. Кирил и Методиј" + }, + { + "date": "2021-08-02", + "name": "Ден на Републиката" + }, + { + "date": "2021-09-08", + "name": "Ден на независноста" + }, + { + "date": "2021-10-11", + "name": "Ден на востанието" + }, + { + "date": "2021-10-23", + "name": "Ден на македонската револуционерна борба" + }, + { + "date": "2021-12-08", + "name": "Св. Климент Охридски" + } + ] + }, + "MQ": { + "name": "Martinique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-22", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "MT": { + "name": "Malta", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2018-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2018-03-19", + "name": "San Ġużepp" + }, + { + "date": "2018-03-30", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2018-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2018-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2018-06-07", + "name": "Sette Giugno" + }, + { + "date": "2018-06-29", + "name": "L-Imnarja" + }, + { + "date": "2018-08-15", + "name": "Santa Marija" + }, + { + "date": "2018-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2018-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2018-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2018-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2018-12-25", + "name": "Il-Milied" + }, + { + "date": "2019-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2019-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2019-03-19", + "name": "San Ġużepp" + }, + { + "date": "2019-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2019-04-19", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2019-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2019-06-07", + "name": "Sette Giugno" + }, + { + "date": "2019-06-29", + "name": "L-Imnarja" + }, + { + "date": "2019-08-15", + "name": "Santa Marija" + }, + { + "date": "2019-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2019-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2019-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2019-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2019-12-25", + "name": "Il-Milied" + }, + { + "date": "2020-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2020-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2020-03-19", + "name": "San Ġużepp" + }, + { + "date": "2020-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2020-04-10", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2020-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2020-06-07", + "name": "Sette Giugno" + }, + { + "date": "2020-06-29", + "name": "L-Imnarja" + }, + { + "date": "2020-08-15", + "name": "Santa Marija" + }, + { + "date": "2020-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2020-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2020-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2020-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2020-12-25", + "name": "Il-Milied" + }, + { + "date": "2021-01-01", + "name": "L-Ewwel tas-Sena" + }, + { + "date": "2021-02-10", + "name": "Nawfraġju ta' San Pawl" + }, + { + "date": "2021-03-19", + "name": "San Ġużepp" + }, + { + "date": "2021-03-31", + "name": "Jum il-Ħelsien" + }, + { + "date": "2021-04-02", + "name": "Il-Ġimgħa l-Kbira" + }, + { + "date": "2021-05-01", + "name": "Jum il-Ħaddiem" + }, + { + "date": "2021-06-07", + "name": "Sette Giugno" + }, + { + "date": "2021-06-29", + "name": "L-Imnarja" + }, + { + "date": "2021-08-15", + "name": "Santa Marija" + }, + { + "date": "2021-09-08", + "name": "Jum il-Vitorja" + }, + { + "date": "2021-09-21", + "name": "Jum l-Indipendenza" + }, + { + "date": "2021-12-08", + "name": "Il-Kunċizzjoni" + }, + { + "date": "2021-12-13", + "name": "Jum ir-Repubblika" + }, + { + "date": "2021-12-25", + "name": "Il-Milied" + } + ] + }, + "MW": { + "name": "Malawi", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2018-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2018-03-05", + "name": "Martyrs' Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2018-06-15", + "name": "Eid al Fitri" + }, + { + "date": "2018-07-06", + "name": "Independence Day" + }, + { + "date": "2018-10-15", + "name": "Mother's Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2019-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2019-03-04", + "name": "Martyrs' Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2019-06-04", + "name": "Eid al Fitri" + }, + { + "date": "2019-07-06", + "name": "Independence Day" + }, + { + "date": "2019-07-08", + "name": "Independence Day (substitute day)" + }, + { + "date": "2019-10-15", + "name": "Mother's Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2020-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2020-05-24", + "name": "Eid al Fitri" + }, + { + "date": "2020-05-25", + "name": "Eid al Fitri (substitute day)" + }, + { + "date": "2020-07-06", + "name": "Independence Day" + }, + { + "date": "2020-10-15", + "name": "Mother's Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-15", + "name": "John Chilembwe Day" + }, + { + "date": "2021-03-03", + "name": "Martyrs' Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-03", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "Eid al Fitri" + }, + { + "date": "2021-05-14", + "name": "Kamuzu Day" + }, + { + "date": "2021-07-06", + "name": "Independence Day" + }, + { + "date": "2021-10-15", + "name": "Mother's Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + } + ] + }, + "MX": { + "name": "México", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-05", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2018-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2018-03-19", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2018-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2018-11-19", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2018-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2018-12-01", + "name": "Transmisión del Poder Ejecutivo Federal" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-02-04", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2019-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2019-03-18", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2019-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2019-11-18", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2019-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-03", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2020-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2020-03-16", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2020-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2020-11-16", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2020-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-01", + "name": "Día de la Constitución (día libre)" + }, + { + "date": "2021-02-05", + "name": "Día de la Constitución" + }, + { + "date": "2021-03-15", + "name": "Natalicio de Benito Juárez (día libre)" + }, + { + "date": "2021-03-21", + "name": "Natalicio de Benito Juárez" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-09-16", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-15", + "name": "Día de la Revolución (día libre)" + }, + { + "date": "2021-11-20", + "name": "Día de la Revolución" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "MZ": { + "name": "Moçambique", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2018-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2018-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2018-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2018-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2018-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2018-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2018-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2018-12-25", + "name": "Dia da Família" + }, + { + "date": "2019-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2019-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2019-02-04", + "name": "Feriado Obrigatório" + }, + { + "date": "2019-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2019-04-08", + "name": "Feriado Obrigatório" + }, + { + "date": "2019-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2019-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2019-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2019-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2019-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2019-12-25", + "name": "Dia da Família" + }, + { + "date": "2020-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2020-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2020-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2020-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2020-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2020-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2020-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2020-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2020-10-05", + "name": "Feriado Obrigatório" + }, + { + "date": "2020-12-25", + "name": "Dia da Família" + }, + { + "date": "2021-01-01", + "name": "Dia da Fraternidade universal" + }, + { + "date": "2021-02-03", + "name": "Dia dos heróis moçambicanos" + }, + { + "date": "2021-04-07", + "name": "Dia da Mulher Moçambicana" + }, + { + "date": "2021-05-01", + "name": "Dia Internacional dos Trabalhadores" + }, + { + "date": "2021-06-25", + "name": "Dia da Independência Nacional" + }, + { + "date": "2021-09-07", + "name": "Dia da Vitória" + }, + { + "date": "2021-09-25", + "name": "Dia das Forças Armadas de Libertação Nacional" + }, + { + "date": "2021-10-04", + "name": "Dia da Paz e Reconciliação" + }, + { + "date": "2021-12-25", + "name": "Dia da Família" + } + ] + }, + "NA": { + "name": "Namibia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-21", + "name": "Independence Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Workers Day" + }, + { + "date": "2018-05-04", + "name": "Cassinga Day" + }, + { + "date": "2018-05-10", + "name": "Ascension Day" + }, + { + "date": "2018-05-25", + "name": "Africa Day" + }, + { + "date": "2018-08-26", + "name": "Heroes' Day" + }, + { + "date": "2018-08-27", + "name": "Public Holiday" + }, + { + "date": "2018-12-10", + "name": "Human Rights Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-21", + "name": "Independence Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Workers Day" + }, + { + "date": "2019-05-04", + "name": "Cassinga Day" + }, + { + "date": "2019-05-25", + "name": "Africa Day" + }, + { + "date": "2019-05-30", + "name": "Ascension Day" + }, + { + "date": "2019-08-26", + "name": "Heroes' Day" + }, + { + "date": "2019-12-10", + "name": "Human Rights Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-21", + "name": "Independence Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Workers Day" + }, + { + "date": "2020-05-04", + "name": "Cassinga Day" + }, + { + "date": "2020-05-21", + "name": "Ascension Day" + }, + { + "date": "2020-05-25", + "name": "Africa Day" + }, + { + "date": "2020-08-26", + "name": "Heroes' Day" + }, + { + "date": "2020-12-10", + "name": "Human Rights Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-21", + "name": "Independence Day" + }, + { + "date": "2021-03-22", + "name": "Public Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Workers Day" + }, + { + "date": "2021-05-04", + "name": "Cassinga Day" + }, + { + "date": "2021-05-13", + "name": "Ascension Day" + }, + { + "date": "2021-05-25", + "name": "Africa Day" + }, + { + "date": "2021-08-26", + "name": "Heroes' Day" + }, + { + "date": "2021-12-10", + "name": "Human Rights Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" + } + ] + }, + "NI": { + "name": "Nicaragua", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-01", + "name": "Día del niño" + }, + { + "date": "2018-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2018-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-01", + "name": "Día del niño" + }, + { + "date": "2019-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2019-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-01", + "name": "Día del niño" + }, + { + "date": "2020-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2020-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-01", + "name": "Día del niño" + }, + { + "date": "2021-07-19", + "name": "Triunfo de la Revolución Popular" + }, + { + "date": "2021-09-14", + "name": "Batalla de San Jacinto" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "NL": { + "name": "Nederland", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2018-04-02", + "name": "Paasmaandag" + }, + { + "date": "2018-04-27", + "name": "Koningsdag" + }, + { + "date": "2018-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2018-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2018-05-10", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2018-05-21", + "name": "Pinkstermaandag" + }, + { + "date": "2018-12-25", + "name": "Kerstmis" + }, + { + "date": "2018-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2018-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2019-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2019-04-22", + "name": "Paasmaandag" + }, + { + "date": "2019-04-27", + "name": "Koningsdag" + }, + { + "date": "2019-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2019-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2019-05-30", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2019-06-10", + "name": "Pinkstermaandag" + }, + { + "date": "2019-12-25", + "name": "Kerstmis" + }, + { + "date": "2019-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2019-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2020-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2020-04-13", + "name": "Paasmaandag" + }, + { + "date": "2020-04-27", + "name": "Koningsdag" + }, + { + "date": "2020-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2020-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2020-05-21", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2020-06-01", + "name": "Pinkstermaandag" + }, + { + "date": "2020-12-25", + "name": "Kerstmis" + }, + { + "date": "2020-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2020-12-31", + "name": "Oudejaarsavond" + }, + { + "date": "2021-01-01", + "name": "Nieuwjaar" + }, + { + "date": "2021-04-05", + "name": "Paasmaandag" + }, + { + "date": "2021-04-27", + "name": "Koningsdag" + }, + { + "date": "2021-05-04", + "name": "Nationale Dodenherdenking" + }, + { + "date": "2021-05-05", + "name": "Bevrijdingsdag" + }, + { + "date": "2021-05-13", + "name": "O.L.H. Hemelvaart" + }, + { + "date": "2021-05-24", + "name": "Pinkstermaandag" + }, + { + "date": "2021-12-25", + "name": "Kerstmis" + }, + { + "date": "2021-12-26", + "name": "Tweede kerstdag" + }, + { + "date": "2021-12-31", + "name": "Oudejaarsavond" + } + ] + }, + "NO": { + "name": "Norge", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2018-03-29", + "name": "Skjærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Første påskedag" + }, + { + "date": "2018-04-02", + "name": "Andre påskedag" + }, + { + "date": "2018-05-01", + "name": "Første mai" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2018-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2018-05-20", + "name": "Første pinsedag" + }, + { + "date": "2018-05-21", + "name": "Andre pinsedag" + }, + { + "date": "2018-12-24", + "name": "Julaften" + }, + { + "date": "2018-12-25", + "name": "Første Juledag" + }, + { + "date": "2018-12-26", + "name": "Andre juledag" + }, + { + "date": "2018-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2019-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2019-04-18", + "name": "Skjærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Første påskedag" + }, + { + "date": "2019-04-22", + "name": "Andre påskedag" + }, + { + "date": "2019-05-01", + "name": "Første mai" + }, + { + "date": "2019-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Første pinsedag" + }, + { + "date": "2019-06-10", + "name": "Andre pinsedag" + }, + { + "date": "2019-12-24", + "name": "Julaften" + }, + { + "date": "2019-12-25", + "name": "Første Juledag" + }, + { + "date": "2019-12-26", + "name": "Andre juledag" + }, + { + "date": "2019-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2020-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2020-04-09", + "name": "Skjærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Første påskedag" + }, + { + "date": "2020-04-13", + "name": "Andre påskedag" + }, + { + "date": "2020-05-01", + "name": "Første mai" + }, + { + "date": "2020-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Første pinsedag" + }, + { + "date": "2020-06-01", + "name": "Andre pinsedag" + }, + { + "date": "2020-12-24", + "name": "Julaften" + }, + { + "date": "2020-12-25", + "name": "Første Juledag" + }, + { + "date": "2020-12-26", + "name": "Andre juledag" + }, + { + "date": "2020-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2021-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2021-04-01", + "name": "Skjærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Første påskedag" + }, + { + "date": "2021-04-05", + "name": "Andre påskedag" + }, + { + "date": "2021-05-01", + "name": "Første mai" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2021-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2021-05-23", + "name": "Første pinsedag" + }, + { + "date": "2021-05-24", + "name": "Andre pinsedag" + }, + { + "date": "2021-12-24", + "name": "Julaften" + }, + { + "date": "2021-12-25", + "name": "Første Juledag" + }, + { + "date": "2021-12-26", + "name": "Andre juledag" + }, + { + "date": "2021-12-31", + "name": "Nyttårsaften" + } + ] + }, + "NZ": { + "name": "New Zealand", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2018-02-06", + "name": "Waitangi Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-25", + "name": "ANZAC Day" + }, + { + "date": "2018-06-04", + "name": "Queen's Birthday" + }, + { + "date": "2018-10-22", + "name": "Labour Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2019-02-06", + "name": "Waitangi Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-25", + "name": "ANZAC Day" + }, + { + "date": "2019-06-03", + "name": "Queen's Birthday" + }, + { + "date": "2019-10-28", + "name": "Labour Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2020-02-06", + "name": "Waitangi Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-27", + "name": "ANZAC Day" + }, + { + "date": "2020-06-01", + "name": "Queen's Birthday" + }, + { + "date": "2020-10-26", + "name": "Labour Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-12-28", + "name": "Boxing Day (substitute day)" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "Day after New Year's Day" + }, + { + "date": "2021-01-04", + "name": "Day after New Year's Day (substitute day)" + }, + { + "date": "2021-02-08", + "name": "Waitangi Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-26", + "name": "ANZAC Day" + }, + { + "date": "2021-06-07", + "name": "Queen's Birthday" + }, + { + "date": "2021-10-25", + "name": "Labour Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-12-27", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-27", + "name": "Boxing Day (substitute day)" + } + ] + }, + "PA": { + "name": "Panamá", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2018-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2018-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-08", + "name": "Día de la Madre" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-01", + "name": "Presidential Inauguration" + }, + { + "date": "2019-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2019-11-04", + "name": "Día de la Separación (de Colombia) (día sustituto)" + }, + { + "date": "2019-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2019-11-11", + "name": "Primer Grito de Independencia (día sustituto)" + }, + { + "date": "2019-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-08", + "name": "Día de la Madre" + }, + { + "date": "2019-12-09", + "name": "Día de la Madre (día sustituto)" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2020-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2020-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-08", + "name": "Día de la Madre" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-01-09", + "name": "Día de los Mártires Caídos en la Gesta Patriótica" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-11-03", + "name": "Día de la Separación (de Colombia)" + }, + { + "date": "2021-11-10", + "name": "Primer Grito de Independencia" + }, + { + "date": "2021-11-28", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-29", + "name": "Día de la Independencia (día sustituto)" + }, + { + "date": "2021-12-08", + "name": "Día de la Madre" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "PE": { + "name": "Perú", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2018-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2018-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2018-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2018-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2018-11-01", + "name": "Todos los Santos" + }, + { + "date": "2018-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2019-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2019-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2019-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2019-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2019-11-01", + "name": "Todos los Santos" + }, + { + "date": "2019-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2020-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2020-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2020-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2020-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2020-11-01", + "name": "Todos los Santos" + }, + { + "date": "2020-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-29", + "name": "San Pedro y San Pablo" + }, + { + "date": "2021-07-28", + "name": "Día de la Independencia" + }, + { + "date": "2021-07-29", + "name": "Día de la Independencia" + }, + { + "date": "2021-08-30", + "name": "Día de Santa Rosa de Lima" + }, + { + "date": "2021-10-08", + "name": "Combate de Angamos" + }, + { + "date": "2021-11-01", + "name": "Todos los Santos" + }, + { + "date": "2021-12-08", + "name": "La inmaculada concepción" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "PL": { + "name": "Polska", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nowy Rok" + }, + { + "date": "2018-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2018-04-01", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2018-04-02", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2018-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2018-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2018-05-20", + "name": "Zielone Świątki" + }, + { + "date": "2018-05-31", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2018-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2018-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2018-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2018-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2018-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2019-01-01", + "name": "Nowy Rok" + }, + { + "date": "2019-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2019-04-21", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2019-04-22", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2019-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2019-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2019-06-09", + "name": "Zielone Świątki" + }, + { + "date": "2019-06-20", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2019-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2019-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2019-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2019-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2019-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2020-01-01", + "name": "Nowy Rok" + }, + { + "date": "2020-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2020-04-12", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2020-04-13", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2020-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2020-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2020-05-31", + "name": "Zielone Świątki" + }, + { + "date": "2020-06-11", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2020-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2020-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2020-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2020-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2020-12-26", + "name": "Drugi dzień Bożego Narodzenia" + }, + { + "date": "2021-01-01", + "name": "Nowy Rok" + }, + { + "date": "2021-01-06", + "name": "Święto Trzech Króli" + }, + { + "date": "2021-04-04", + "name": "Niedziela Wielkanocna" + }, + { + "date": "2021-04-05", + "name": "Drugi dzień Wielkanocy" + }, + { + "date": "2021-05-01", + "name": "Święto Państwowe; Święto Pracy" + }, + { + "date": "2021-05-03", + "name": "Święto Narodowe Trzeciego Maja" + }, + { + "date": "2021-05-23", + "name": "Zielone Świątki" + }, + { + "date": "2021-06-03", + "name": "Dzień Bożego Ciała" + }, + { + "date": "2021-08-15", + "name": "Wniebowzięcie Najświętszej Maryi Panny" + }, + { + "date": "2021-11-01", + "name": "Wszystkich Świętych" + }, + { + "date": "2021-11-11", + "name": "Narodowe Święto Niepodległości" + }, + { + "date": "2021-12-25", + "name": "Pierwszy dzień Bożego Narodzenia" + }, + { + "date": "2021-12-26", + "name": "Drugi dzień Bożego Narodzenia" + } + ] + }, + "PT": { + "name": "Portugal", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Ano Novo" + }, + { + "date": "2018-03-30", + "name": "Sexta-Feira Santa" + }, + { + "date": "2018-04-01", + "name": "Páscoa" + }, + { + "date": "2018-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2018-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2018-05-31", + "name": "Corpo de Deus" + }, + { + "date": "2018-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2018-08-15", + "name": "Assunção" + }, + { + "date": "2018-10-05", + "name": "Implantação da República" + }, + { + "date": "2018-11-01", + "name": "Todos os santos" + }, + { + "date": "2018-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2018-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2018-12-25", + "name": "Natal" + }, + { + "date": "2019-01-01", + "name": "Ano Novo" + }, + { + "date": "2019-04-19", + "name": "Sexta-Feira Santa" + }, + { + "date": "2019-04-21", + "name": "Páscoa" + }, + { + "date": "2019-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2019-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2019-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2019-06-20", + "name": "Corpo de Deus" + }, + { + "date": "2019-08-15", + "name": "Assunção" + }, + { + "date": "2019-10-05", + "name": "Implantação da República" + }, + { + "date": "2019-11-01", + "name": "Todos os santos" + }, + { + "date": "2019-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2019-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2019-12-25", + "name": "Natal" + }, + { + "date": "2020-01-01", + "name": "Ano Novo" + }, + { + "date": "2020-04-10", + "name": "Sexta-Feira Santa" + }, + { + "date": "2020-04-12", + "name": "Páscoa" + }, + { + "date": "2020-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2020-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2020-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2020-06-11", + "name": "Corpo de Deus" + }, + { + "date": "2020-08-15", + "name": "Assunção" + }, + { + "date": "2020-10-05", + "name": "Implantação da República" + }, + { + "date": "2020-11-01", + "name": "Todos os santos" + }, + { + "date": "2020-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2020-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2020-12-25", + "name": "Natal" + }, + { + "date": "2021-01-01", + "name": "Ano Novo" + }, + { + "date": "2021-04-02", + "name": "Sexta-Feira Santa" + }, + { + "date": "2021-04-04", + "name": "Páscoa" + }, + { + "date": "2021-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2021-05-01", + "name": "Dia do trabalhador" + }, + { + "date": "2021-06-03", + "name": "Corpo de Deus" + }, + { + "date": "2021-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2021-08-15", + "name": "Assunção" + }, + { + "date": "2021-10-05", + "name": "Implantação da República" + }, + { + "date": "2021-11-01", + "name": "Todos os santos" + }, + { + "date": "2021-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2021-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2021-12-25", + "name": "Natal" + }, + { + "date": "2022-01-01", + "name": "Ano Novo" + }, + { + "date": "2022-04-15", + "name": "Sexta-Feira Santa" + }, + { + "date": "2022-04-17", + "name": "Páscoa" + }, + { + "date": "2022-04-25", + "name": "Dia da Liberdade" + }, + { + "date": "2022-05-01", + "name": "Dia do Trabalhador" + }, + { + "date": "2022-06-10", + "name": "Dia de Portugal" + }, + { + "date": "2022-06-16", + "name": "Corpo de Deus" + }, + { + "date": "2022-08-15", + "name": "Assunção de Nossa Senhora" + }, + { + "date": "2022-10-05", + "name": "Implantação da República" + }, + { + "date": "2022-11-01", + "name": "Dia de Todos os Santos" + }, + { + "date": "2022-12-01", + "name": "Restauração da Independência" + }, + { + "date": "2022-12-08", + "name": "Imaculada Conceição" + }, + { + "date": "2022-12-25", + "name": "Natal" + } + ] + }, + "PY": { + "name": "Paraguay", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2018-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-06-18", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2018-08-15", + "name": "Asunción" + }, + { + "date": "2018-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2018-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2019-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-06-17", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2019-08-15", + "name": "Asunción" + }, + { + "date": "2019-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2019-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2020-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-06-15", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2020-08-15", + "name": "Asunción" + }, + { + "date": "2020-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2020-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-05-14", + "name": "Día de la Independencia" + }, + { + "date": "2021-05-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-06-14", + "name": "Día de la Paz del Chaco" + }, + { + "date": "2021-08-15", + "name": "Asunción" + }, + { + "date": "2021-09-29", + "name": "Victoria de Boquerón" + }, + { + "date": "2021-12-08", + "name": "Virgen de Caacupé" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "RE": { + "name": "Réunion", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-11", + "name": "Armistice 1918" + }, + { + "date": "2019-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-11-11", + "name": "Armistice 1918" + }, + { + "date": "2020-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2021-05-13", + "name": "Ascension" + }, + { + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-20", + "name": "Abolition de l’esclavage" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "RO": { + "name": "Romania", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Anul nou" + }, + { + "date": "2018-04-08", + "name": "Paștele" + }, + { + "date": "2018-04-09", + "name": "Două zi de Pasti" + }, + { + "date": "2018-05-01", + "name": "Ziua muncii" + }, + { + "date": "2018-05-27", + "name": "Rusaliile" + }, + { + "date": "2018-05-28", + "name": "Două zi de Rusalii" + }, + { + "date": "2018-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2018-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2018-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2018-12-25", + "name": "Crăciunul" + }, + { + "date": "2018-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2019-01-01", + "name": "Anul nou" + }, + { + "date": "2019-04-28", + "name": "Paștele" + }, + { + "date": "2019-04-29", + "name": "Două zi de Pasti" + }, + { + "date": "2019-05-01", + "name": "Ziua muncii" + }, + { + "date": "2019-06-16", + "name": "Rusaliile" + }, + { + "date": "2019-06-17", + "name": "Două zi de Rusalii" + }, + { + "date": "2019-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2019-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2019-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2019-12-25", + "name": "Crăciunul" + }, + { + "date": "2019-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2020-01-01", + "name": "Anul nou" + }, + { + "date": "2020-04-19", + "name": "Paștele" + }, + { + "date": "2020-04-20", + "name": "Două zi de Pasti" + }, + { + "date": "2020-05-01", + "name": "Ziua muncii" + }, + { + "date": "2020-06-07", + "name": "Rusaliile" + }, + { + "date": "2020-06-08", + "name": "Două zi de Rusalii" + }, + { + "date": "2020-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2020-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2020-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2020-12-25", + "name": "Crăciunul" + }, + { + "date": "2020-12-26", + "name": "Două zi de Crăciun" + }, + { + "date": "2021-01-01", + "name": "Anul nou" + }, + { + "date": "2021-05-01", + "name": "Ziua muncii" + }, + { + "date": "2021-05-02", + "name": "Paștele" + }, + { + "date": "2021-05-03", + "name": "Două zi de Pasti" + }, + { + "date": "2021-06-20", + "name": "Rusaliile" + }, + { + "date": "2021-06-21", + "name": "Două zi de Rusalii" + }, + { + "date": "2021-08-15", + "name": "Adormirea Maicii Domnului" + }, + { + "date": "2021-11-30", + "name": "Sfântul Andrei" + }, + { + "date": "2021-12-01", + "name": "Ziua națională, Ziua Marii Uniri" + }, + { + "date": "2021-12-25", + "name": "Crăciunul" + }, + { + "date": "2021-12-26", + "name": "Două zi de Crăciun" + } + ] + }, + "RS": { + "name": "Република Србија", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Нова година" + }, + { + "date": "2018-01-02", + "name": "Нова година" + }, + { + "date": "2018-01-07", + "name": "Божић" + }, + { + "date": "2018-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2018-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2018-04-06", + "name": "Велики петак" + }, + { + "date": "2018-04-08", + "name": "Васкрс" + }, + { + "date": "2018-04-09", + "name": "Васкрсни понедељак" + }, + { + "date": "2018-05-01", + "name": "Празник рада" + }, + { + "date": "2018-05-02", + "name": "Празник рада" + }, + { + "date": "2018-11-12", + "name": "Дан примирја" + }, + { + "date": "2019-01-01", + "name": "Нова година" + }, + { + "date": "2019-01-02", + "name": "Нова година" + }, + { + "date": "2019-01-07", + "name": "Божић" + }, + { + "date": "2019-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2019-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2019-04-26", + "name": "Велики петак" + }, + { + "date": "2019-04-28", + "name": "Васкрс" + }, + { + "date": "2019-04-29", + "name": "Васкрсни понедељак" + }, + { + "date": "2019-05-01", + "name": "Празник рада" + }, + { + "date": "2019-05-02", + "name": "Празник рада" + }, + { + "date": "2019-11-11", + "name": "Дан примирја" + }, + { + "date": "2020-01-01", + "name": "Нова година" + }, + { + "date": "2020-01-02", + "name": "Нова година" + }, + { + "date": "2020-01-07", + "name": "Божић" + }, + { + "date": "2020-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2020-02-17", + "name": "Дан државности Србије" + }, + { + "date": "2020-04-17", + "name": "Велики петак" + }, + { + "date": "2020-04-19", + "name": "Васкрс" + }, + { + "date": "2020-04-20", + "name": "Васкрсни понедељак" + }, + { + "date": "2020-05-01", + "name": "Празник рада" + }, + { + "date": "2020-05-02", + "name": "Празник рада" + }, + { + "date": "2020-11-11", + "name": "Дан примирја" + }, + { + "date": "2021-01-01", + "name": "Нова година" + }, + { + "date": "2021-01-02", + "name": "Нова година" + }, + { + "date": "2021-01-07", + "name": "Божић" + }, + { + "date": "2021-02-15", + "name": "Дан државности Србије" + }, + { + "date": "2021-02-16", + "name": "Дан државности Србије" + }, + { + "date": "2021-04-30", + "name": "Велики петак" + }, + { + "date": "2021-05-01", + "name": "Празник рада" + }, + { + "date": "2021-05-02", + "name": "Васкрс" + }, + { + "date": "2021-05-03", + "name": "Празник рада" + }, + { + "date": "2021-05-03", + "name": "Васкрсни понедељак" + }, + { + "date": "2021-11-11", + "name": "Дан примирја" + } + ] + }, + "RU": { + "name": "Россия", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новый год" + }, + { + "date": "2018-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2018-01-07", + "name": "Рождество Христово" + }, + { + "date": "2018-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2018-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2018-03-08", + "name": "Международный женский день" + }, + { + "date": "2018-05-01", + "name": "День весны и труда" + }, + { + "date": "2018-05-09", + "name": "День Победы" + }, + { + "date": "2018-06-12", + "name": "День России" + }, + { + "date": "2018-08-22", + "name": "День Государственного флага" + }, + { + "date": "2018-11-04", + "name": "День народного единства" + }, + { + "date": "2019-01-01", + "name": "Новый год" + }, + { + "date": "2019-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2019-01-07", + "name": "Рождество Христово" + }, + { + "date": "2019-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2019-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2019-03-08", + "name": "Международный женский день" + }, + { + "date": "2019-05-01", + "name": "День весны и труда" + }, + { + "date": "2019-05-09", + "name": "День Победы" + }, + { + "date": "2019-06-12", + "name": "День России" + }, + { + "date": "2019-08-22", + "name": "День Государственного флага" + }, + { + "date": "2019-11-04", + "name": "День народного единства" + }, + { + "date": "2020-01-01", + "name": "Новый год" + }, + { + "date": "2020-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2020-01-07", + "name": "Рождество Христово" + }, + { + "date": "2020-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2020-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2020-03-08", + "name": "Международный женский день" + }, + { + "date": "2020-05-01", + "name": "День весны и труда" + }, + { + "date": "2020-05-09", + "name": "День Победы" + }, + { + "date": "2020-06-12", + "name": "День России" + }, + { + "date": "2020-08-22", + "name": "День Государственного флага" + }, + { + "date": "2020-11-04", + "name": "День народного единства" + }, + { + "date": "2021-01-01", + "name": "Новый год" + }, + { + "date": "2021-01-02", + "name": "Новогодние каникулы" + }, + { + "date": "2021-01-07", + "name": "Рождество Христово" + }, + { + "date": "2021-01-08", + "name": "Новогодние каникулы" + }, + { + "date": "2021-02-23", + "name": "День защитника Отечества" + }, + { + "date": "2021-03-08", + "name": "Международный женский день" + }, + { + "date": "2021-05-01", + "name": "День весны и труда" + }, + { + "date": "2021-05-09", + "name": "День Победы" + }, + { + "date": "2021-06-12", + "name": "День России" + }, + { + "date": "2021-08-22", + "name": "День Государственного флага" + }, + { + "date": "2021-11-04", + "name": "День народного единства" + } + ] + }, + "RW": { + "name": "Repubulika y'u Rwanda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2018-02-01", + "name": "Heroes Day" + }, + { + "date": "2018-03-30", + "name": "Vendredi saint" + }, + { + "date": "2018-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-06-15", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2018-08-03", + "name": "Umuganura" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-21", + "name": "Fête du mouton" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2018-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2019-02-01", + "name": "Heroes Day" + }, + { + "date": "2019-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2019-04-19", + "name": "Vendredi saint" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-04", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2019-08-02", + "name": "Umuganura" + }, + { + "date": "2019-08-11", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2019-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2020-02-01", + "name": "Heroes Day" + }, + { + "date": "2020-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2020-04-10", + "name": "Vendredi saint" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-24", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2020-07-31", + "name": "Fête du mouton" + }, + { + "date": "2020-08-07", + "name": "Umuganura" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2020-12-26", + "name": "Lendemain de Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-02", + "name": "Jour férié légaux" + }, + { + "date": "2021-02-01", + "name": "Heroes Day" + }, + { + "date": "2021-04-02", + "name": "Vendredi saint" + }, + { + "date": "2021-04-07", + "name": "Jour de Mémorial du Génocide" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-13", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-07-01", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-07-04", + "name": "Jour de la Libération" + }, + { + "date": "2021-07-20", + "name": "Fête du mouton" + }, + { + "date": "2021-08-06", + "name": "Umuganura" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-12-25", + "name": "Noël" + }, + { + "date": "2021-12-26", + "name": "Lendemain de Noël" + } + ] + }, + "SE": { + "name": "Sverige", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2018-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2018-03-30", + "name": "Långfredagen" + }, + { + "date": "2018-04-02", + "name": "Annandag påsk" + }, + { + "date": "2018-05-01", + "name": "Första Maj" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2018-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2018-06-23", + "name": "Midsommar" + }, + { + "date": "2018-11-03", + "name": "Alla Helgons dag" + }, + { + "date": "2018-12-25", + "name": "Juldagen" + }, + { + "date": "2018-12-26", + "name": "Annandag jul" + }, + { + "date": "2019-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2019-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2019-04-19", + "name": "Långfredagen" + }, + { + "date": "2019-04-22", + "name": "Annandag påsk" + }, + { + "date": "2019-05-01", + "name": "Första Maj" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2019-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2019-06-22", + "name": "Midsommar" + }, + { + "date": "2019-11-02", + "name": "Alla Helgons dag" + }, + { + "date": "2019-12-25", + "name": "Juldagen" + }, + { + "date": "2019-12-26", + "name": "Annandag jul" + }, + { + "date": "2020-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2020-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2020-04-10", + "name": "Långfredagen" + }, + { + "date": "2020-04-13", + "name": "Annandag påsk" + }, + { + "date": "2020-05-01", + "name": "Första Maj" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2020-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2020-06-20", + "name": "Midsommar" + }, + { + "date": "2020-10-31", + "name": "Alla Helgons dag" + }, + { + "date": "2020-12-25", + "name": "Juldagen" + }, + { + "date": "2020-12-26", + "name": "Annandag jul" + }, + { + "date": "2021-01-01", + "name": "Nyårsdagen" + }, + { + "date": "2021-01-06", + "name": "Trettondedag jul" + }, + { + "date": "2021-04-02", + "name": "Långfredagen" + }, + { + "date": "2021-04-05", + "name": "Annandag påsk" + }, + { + "date": "2021-05-01", + "name": "Första Maj" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfärds dag" + }, + { + "date": "2021-06-06", + "name": "Sveriges nationaldag" + }, + { + "date": "2021-06-26", + "name": "Midsommar" + }, + { + "date": "2021-11-06", + "name": "Alla Helgons dag" + }, + { + "date": "2021-12-25", + "name": "Juldagen" + }, + { + "date": "2021-12-26", + "name": "Annandag jul" + } + ] + }, + "SI": { + "name": "Republika Slovenija", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "novo leto" + }, + { + "date": "2018-01-02", + "name": "novo leto" + }, + { + "date": "2018-02-08", + "name": "Prešernov dan" + }, + { + "date": "2018-04-01", + "name": "velika noč" + }, + { + "date": "2018-04-02", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2018-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2018-05-01", + "name": "praznik dela" + }, + { + "date": "2018-05-02", + "name": "praznik dela" + }, + { + "date": "2018-05-20", + "name": "binkošti" + }, + { + "date": "2018-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2018-06-25", + "name": "dan državnosti" + }, + { + "date": "2018-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2018-10-31", + "name": "dan reformacije" + }, + { + "date": "2018-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2018-12-25", + "name": "božič" + }, + { + "date": "2018-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2019-01-01", + "name": "novo leto" + }, + { + "date": "2019-01-02", + "name": "novo leto" + }, + { + "date": "2019-02-08", + "name": "Prešernov dan" + }, + { + "date": "2019-04-21", + "name": "velika noč" + }, + { + "date": "2019-04-22", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2019-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2019-05-01", + "name": "praznik dela" + }, + { + "date": "2019-05-02", + "name": "praznik dela" + }, + { + "date": "2019-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2019-06-09", + "name": "binkošti" + }, + { + "date": "2019-06-25", + "name": "dan državnosti" + }, + { + "date": "2019-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2019-10-31", + "name": "dan reformacije" + }, + { + "date": "2019-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2019-12-25", + "name": "božič" + }, + { + "date": "2019-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2020-01-01", + "name": "novo leto" + }, + { + "date": "2020-01-02", + "name": "novo leto" + }, + { + "date": "2020-02-08", + "name": "Prešernov dan" + }, + { + "date": "2020-04-12", + "name": "velika noč" + }, + { + "date": "2020-04-13", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2020-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2020-05-01", + "name": "praznik dela" + }, + { + "date": "2020-05-02", + "name": "praznik dela" + }, + { + "date": "2020-05-31", + "name": "binkošti" + }, + { + "date": "2020-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2020-06-25", + "name": "dan državnosti" + }, + { + "date": "2020-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2020-10-31", + "name": "dan reformacije" + }, + { + "date": "2020-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2020-12-25", + "name": "božič" + }, + { + "date": "2020-12-26", + "name": "dan samostojnosti in enotnosti" + }, + { + "date": "2021-01-01", + "name": "novo leto" + }, + { + "date": "2021-01-02", + "name": "novo leto" + }, + { + "date": "2021-02-08", + "name": "Prešernov dan" + }, + { + "date": "2021-04-04", + "name": "velika noč" + }, + { + "date": "2021-04-05", + "name": "Velikonočni ponedeljek" + }, + { + "date": "2021-04-27", + "name": "dan upora proti okupatorju" + }, + { + "date": "2021-05-01", + "name": "praznik dela" + }, + { + "date": "2021-05-02", + "name": "praznik dela" + }, + { + "date": "2021-05-23", + "name": "binkošti" + }, + { + "date": "2021-06-08", + "name": "dan Primoža Trubarja" + }, + { + "date": "2021-06-25", + "name": "dan državnosti" + }, + { + "date": "2021-08-15", + "name": "Marijino vnebovzetje" + }, + { + "date": "2021-10-31", + "name": "dan reformacije" + }, + { + "date": "2021-11-01", + "name": "dan spomina na mrtve or dan mrtvih" + }, + { + "date": "2021-12-25", + "name": "božič" + }, + { + "date": "2021-12-26", + "name": "dan samostojnosti in enotnosti" + } + ] + }, + "SJ": { + "name": "Svalbard & Jan Mayen", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2018-03-29", + "name": "Skjærtorsdag" + }, + { + "date": "2018-03-30", + "name": "Langfredag" + }, + { + "date": "2018-04-01", + "name": "Første påskedag" + }, + { + "date": "2018-04-02", + "name": "Andre påskedag" + }, + { + "date": "2018-05-01", + "name": "Første mai" + }, + { + "date": "2018-05-10", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2018-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2018-05-20", + "name": "Første pinsedag" + }, + { + "date": "2018-05-21", + "name": "Andre pinsedag" + }, + { + "date": "2018-12-24", + "name": "Julaften" + }, + { + "date": "2018-12-25", + "name": "Første Juledag" + }, + { + "date": "2018-12-26", + "name": "Andre juledag" + }, + { + "date": "2018-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2019-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2019-04-18", + "name": "Skjærtorsdag" + }, + { + "date": "2019-04-19", + "name": "Langfredag" + }, + { + "date": "2019-04-21", + "name": "Første påskedag" + }, + { + "date": "2019-04-22", + "name": "Andre påskedag" + }, + { + "date": "2019-05-01", + "name": "Første mai" + }, + { + "date": "2019-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2019-05-30", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2019-06-09", + "name": "Første pinsedag" + }, + { + "date": "2019-06-10", + "name": "Andre pinsedag" + }, + { + "date": "2019-12-24", + "name": "Julaften" + }, + { + "date": "2019-12-25", + "name": "Første Juledag" + }, + { + "date": "2019-12-26", + "name": "Andre juledag" + }, + { + "date": "2019-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2020-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2020-04-09", + "name": "Skjærtorsdag" + }, + { + "date": "2020-04-10", + "name": "Langfredag" + }, + { + "date": "2020-04-12", + "name": "Første påskedag" + }, + { + "date": "2020-04-13", + "name": "Andre påskedag" + }, + { + "date": "2020-05-01", + "name": "Første mai" + }, + { + "date": "2020-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2020-05-21", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2020-05-31", + "name": "Første pinsedag" + }, + { + "date": "2020-06-01", + "name": "Andre pinsedag" + }, + { + "date": "2020-12-24", + "name": "Julaften" + }, + { + "date": "2020-12-25", + "name": "Første Juledag" + }, + { + "date": "2020-12-26", + "name": "Andre juledag" + }, + { + "date": "2020-12-31", + "name": "Nyttårsaften" + }, + { + "date": "2021-01-01", + "name": "Første nyttårsdag" + }, + { + "date": "2021-04-01", + "name": "Skjærtorsdag" + }, + { + "date": "2021-04-02", + "name": "Langfredag" + }, + { + "date": "2021-04-04", + "name": "Første påskedag" + }, + { + "date": "2021-04-05", + "name": "Andre påskedag" + }, + { + "date": "2021-05-01", + "name": "Første mai" + }, + { + "date": "2021-05-13", + "name": "Kristi himmelfartsdag" + }, + { + "date": "2021-05-17", + "name": "Grunnlovsdagen" + }, + { + "date": "2021-05-23", + "name": "Første pinsedag" + }, + { + "date": "2021-05-24", + "name": "Andre pinsedag" + }, + { + "date": "2021-12-24", + "name": "Julaften" + }, + { + "date": "2021-12-25", + "name": "Første Juledag" + }, + { + "date": "2021-12-26", + "name": "Andre juledag" + }, + { + "date": "2021-12-31", + "name": "Nyttårsaften" + } + ] + }, + "SK": { + "name": "Slovenská republika", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2018-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2018-03-30", + "name": "Veľkonočný piatok" + }, + { + "date": "2018-04-02", + "name": "Veľkonočný pondelok" + }, + { + "date": "2018-05-01", + "name": "Sviatok práce" + }, + { + "date": "2018-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2018-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2018-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2018-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2018-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2018-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2018-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2018-12-24", + "name": "Štedrý deň" + }, + { + "date": "2018-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2018-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2019-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2019-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2019-04-19", + "name": "Veľkonočný piatok" + }, + { + "date": "2019-04-22", + "name": "Veľkonočný pondelok" + }, + { + "date": "2019-05-01", + "name": "Sviatok práce" + }, + { + "date": "2019-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2019-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2019-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2019-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2019-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2019-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2019-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2019-12-24", + "name": "Štedrý deň" + }, + { + "date": "2019-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2019-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2020-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2020-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2020-04-10", + "name": "Veľkonočný piatok" + }, + { + "date": "2020-04-13", + "name": "Veľkonočný pondelok" + }, + { + "date": "2020-05-01", + "name": "Sviatok práce" + }, + { + "date": "2020-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2020-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2020-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2020-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2020-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2020-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2020-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2020-12-24", + "name": "Štedrý deň" + }, + { + "date": "2020-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2020-12-26", + "name": "Druhý sviatok vianočný" + }, + { + "date": "2021-01-01", + "name": "Deň vzniku Slovenskej republiky" + }, + { + "date": "2021-01-06", + "name": "Zjavenie Pána" + }, + { + "date": "2021-04-02", + "name": "Veľkonočný piatok" + }, + { + "date": "2021-04-05", + "name": "Veľkonočný pondelok" + }, + { + "date": "2021-05-01", + "name": "Sviatok práce" + }, + { + "date": "2021-05-08", + "name": "Deň víťazstva nad fašizmom" + }, + { + "date": "2021-07-05", + "name": "Sviatok svätého Cyrila a Metoda" + }, + { + "date": "2021-08-29", + "name": "Výročie Slovenského národného povstania" + }, + { + "date": "2021-09-01", + "name": "Deň Ústavy" + }, + { + "date": "2021-09-15", + "name": "Sviatok Panny Márie Sedembolestnej" + }, + { + "date": "2021-11-01", + "name": "Sviatok všetkých svätých" + }, + { + "date": "2021-11-17", + "name": "Deň boja za slobodu a demokraciu" + }, + { + "date": "2021-12-24", + "name": "Štedrý deň" + }, + { + "date": "2021-12-25", + "name": "Prvý sviatok vianočný" + }, + { + "date": "2021-12-26", + "name": "Druhý sviatok vianočný" + } + ] + }, + "SM": { + "name": "San Marino", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Capodanno" + }, + { + "date": "2018-01-06", + "name": "Epifania" + }, + { + "date": "2018-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2018-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2018-04-01", + "name": "Domenica di Pasqua" + }, + { + "date": "2018-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2018-05-31", + "name": "Corpus Domini" + }, + { + "date": "2018-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2018-08-15", + "name": "Ferragosto" + }, + { + "date": "2018-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2018-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2018-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2018-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Capodanno" + }, + { + "date": "2019-01-06", + "name": "Epifania" + }, + { + "date": "2019-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2019-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2019-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2019-04-21", + "name": "Domenica di Pasqua" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2019-06-20", + "name": "Corpus Domini" + }, + { + "date": "2019-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2019-08-15", + "name": "Ferragosto" + }, + { + "date": "2019-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2019-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2019-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2019-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Capodanno" + }, + { + "date": "2020-01-06", + "name": "Epifania" + }, + { + "date": "2020-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2020-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2020-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2020-04-12", + "name": "Domenica di Pasqua" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2020-06-11", + "name": "Corpus Domini" + }, + { + "date": "2020-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2020-08-15", + "name": "Ferragosto" + }, + { + "date": "2020-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2020-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2020-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2020-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Capodanno" + }, + { + "date": "2021-01-06", + "name": "Epifania" + }, + { + "date": "2021-02-05", + "name": "Festa di Sant’Agata" + }, + { + "date": "2021-03-25", + "name": "Anniversario dell'Arengo" + }, + { + "date": "2021-04-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2021-04-04", + "name": "Domenica di Pasqua" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-05-01", + "name": "Festa del Lavoro" + }, + { + "date": "2021-06-03", + "name": "Corpus Domini" + }, + { + "date": "2021-07-28", + "name": "Anniversario della caduta del Fascismo e Festa della Libertà" + }, + { + "date": "2021-08-15", + "name": "Ferragosto" + }, + { + "date": "2021-09-03", + "name": "Festa di San Marino e di Fondazione della Repubblica" + }, + { + "date": "2021-10-01", + "name": "Cerimonia di investitura dei Capitani Reggenti" + }, + { + "date": "2021-11-01", + "name": "Tutti i Santi" + }, + { + "date": "2021-11-02", + "name": "Commemorazione dei defunti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "SO": { + "name": "Jamhuuriyadda Federaalka Soomaaliya", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2018-05-01", + "name": "يوم العمال" + }, + { + "date": "2018-06-15", + "name": "عيد الفطر" + }, + { + "date": "2018-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2018-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2018-08-21", + "name": "عيد الأضحى" + }, + { + "date": "2018-09-20", + "name": "عاشوراء" + }, + { + "date": "2018-11-20", + "name": "المولد النبويّ" + }, + { + "date": "2019-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2019-05-01", + "name": "يوم العمال" + }, + { + "date": "2019-06-04", + "name": "عيد الفطر" + }, + { + "date": "2019-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2019-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2019-08-11", + "name": "عيد الأضحى" + }, + { + "date": "2019-09-09", + "name": "عاشوراء" + }, + { + "date": "2019-11-09", + "name": "المولد النبويّ" + }, + { + "date": "2020-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2020-05-01", + "name": "يوم العمال" + }, + { + "date": "2020-05-24", + "name": "عيد الفطر" + }, + { + "date": "2020-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2020-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2020-07-31", + "name": "عيد الأضحى" + }, + { + "date": "2020-08-29", + "name": "عاشوراء" + }, + { + "date": "2020-10-29", + "name": "المولد النبويّ" + }, + { + "date": "2021-01-01", + "name": "عيد رأس السنة" + }, + { + "date": "2021-05-01", + "name": "يوم العمال" + }, + { + "date": "2021-05-13", + "name": "عيد الفطر" + }, + { + "date": "2021-06-26", + "name": "استقلال الصومال البريطاني" + }, + { + "date": "2021-07-01", + "name": "عيد الاستقلال" + }, + { + "date": "2021-07-20", + "name": "عيد الأضحى" + }, + { + "date": "2021-08-18", + "name": "عاشوراء" + }, + { + "date": "2021-10-18", + "name": "المولد النبويّ" + } + ] + }, + "SS": { + "name": "South Sudan", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2018-03-08", + "name": "International Women's Day" + }, + { + "date": "2018-05-16", + "name": "SPLA Day" + }, + { + "date": "2018-06-16", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-07-09", + "name": "Independence Day" + }, + { + "date": "2018-07-30", + "name": "Martyrs Day" + }, + { + "date": "2018-08-23", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-28", + "name": "Republic Day" + }, + { + "date": "2018-12-31", + "name": "New Year's Eve" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2019-03-08", + "name": "International Women's Day" + }, + { + "date": "2019-05-16", + "name": "SPLA Day" + }, + { + "date": "2019-06-05", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-07-09", + "name": "Independence Day" + }, + { + "date": "2019-07-30", + "name": "Martyrs Day" + }, + { + "date": "2019-08-13", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-28", + "name": "Republic Day" + }, + { + "date": "2019-12-31", + "name": "New Year's Eve" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2020-03-08", + "name": "International Women's Day" + }, + { + "date": "2020-05-16", + "name": "SPLA Day" + }, + { + "date": "2020-05-25", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-07-09", + "name": "Independence Day" + }, + { + "date": "2020-07-30", + "name": "Martyrs Day" + }, + { + "date": "2020-08-02", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-28", + "name": "Republic Day" + }, + { + "date": "2020-12-31", + "name": "New Year's Eve" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-09", + "name": "Peace Agreement Day" + }, + { + "date": "2021-03-08", + "name": "International Women's Day" + }, + { + "date": "2021-05-14", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-05-16", + "name": "SPLA Day" + }, + { + "date": "2021-07-09", + "name": "Independence Day" + }, + { + "date": "2021-07-22", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-07-30", + "name": "Martyrs Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-28", + "name": "Republic Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Eve" + } + ] + }, + "SV": { + "name": "El Salvador", + "bank_holidays": [ + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-03-31", + "name": "Sabado Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2018-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2018-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-20", + "name": "Sabado Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2019-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2019-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-11", + "name": "Sabado Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2020-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2020-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-03", + "name": "Sabado Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-08-06", + "name": "Celebración del Divino Salvador del Mundo" + }, + { + "date": "2021-09-15", + "name": "Día de la Independencia" + }, + { + "date": "2021-11-02", + "name": "Día de los Difuntos" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "TG": { + "name": "République togolaise", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-01-13", + "name": "Jour de la libération" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-06-16", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2018-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-08-22", + "name": "Fête du mouton" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-21", + "name": "Mawlid" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-01-13", + "name": "Jour de la libération" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-06-05", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2019-08-12", + "name": "Fête du mouton" + }, + { + "date": "2019-08-15", + "name": "Assomption" + }, + { + "date": "2019-11-01", + "name": "Toussaint" + }, + { + "date": "2019-11-10", + "name": "Mawlid" + }, + { + "date": "2019-12-25", + "name": "Noël" + }, + { + "date": "2020-01-01", + "name": "Nouvel An" + }, + { + "date": "2020-01-13", + "name": "Jour de la libération" + }, + { + "date": "2020-04-13", + "name": "Lundi de Pâques" + }, + { + "date": "2020-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2020-05-01", + "name": "Fête du travail" + }, + { + "date": "2020-05-25", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2020-08-01", + "name": "Fête du mouton" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, + { + "date": "2020-10-30", + "name": "Mawlid" + }, + { + "date": "2020-11-01", + "name": "Toussaint" + }, + { + "date": "2020-12-25", + "name": "Noël" + }, + { + "date": "2021-01-01", + "name": "Nouvel An" + }, + { + "date": "2021-01-13", + "name": "Jour de la libération" + }, + { + "date": "2021-04-05", + "name": "Lundi de Pâques" + }, + { + "date": "2021-04-27", + "name": "Jour de l'Indépendance" + }, + { + "date": "2021-05-01", + "name": "Fête du travail" + }, + { + "date": "2021-05-14", + "name": "Fête de fin du Ramadan" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-06-21", + "name": "Journée des Martyrs" + }, + { + "date": "2021-07-21", + "name": "Fête du mouton" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-10-19", + "name": "Mawlid" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-12-25", + "name": "Noël" + } + ] + }, + "TR": { + "name": "Türkiye", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Yılbaşı" + }, + { + "date": "2018-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2018-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2018-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2018-06-15", + "name": "Bayramı" + }, + { + "date": "2018-08-21", + "name": "Kurban Bayramı" + }, + { + "date": "2018-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2018-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2019-01-01", + "name": "Yılbaşı" + }, + { + "date": "2019-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2019-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2019-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2019-06-04", + "name": "Bayramı" + }, + { + "date": "2019-08-11", + "name": "Kurban Bayramı" + }, + { + "date": "2019-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2019-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2020-01-01", + "name": "Yılbaşı" + }, + { + "date": "2020-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2020-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2020-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2020-05-24", + "name": "Bayramı" + }, + { + "date": "2020-07-31", + "name": "Kurban Bayramı" + }, + { + "date": "2020-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2020-10-29", + "name": "Cumhuriyet Bayramı" + }, + { + "date": "2021-01-01", + "name": "Yılbaşı" + }, + { + "date": "2021-04-23", + "name": "Ulusal Egemenlik ve Çocuk Bayramı" + }, + { + "date": "2021-05-01", + "name": "Emek ve Dayanışma Günü" + }, + { + "date": "2021-05-13", + "name": "Bayramı" + }, + { + "date": "2021-05-19", + "name": "Atatürk'ü Anma Gençlik ve Spor Bayramı" + }, + { + "date": "2021-07-20", + "name": "Kurban Bayramı" + }, + { + "date": "2021-08-30", + "name": "Zafer Bayramı" + }, + { + "date": "2021-10-29", + "name": "Cumhuriyet Bayramı" + } + ] + }, + "TZ": { + "name": "Tanzania", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-07", + "name": "Karume Day" + }, + { + "date": "2018-04-26", + "name": "Union Day" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-15", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2018-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2018-08-21", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-10-14", + "name": "Nyerere Day" + }, + { + "date": "2018-11-20", + "name": "Maulid Day" + }, + { + "date": "2018-12-09", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2019-04-07", + "name": "Karume Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-04-26", + "name": "Union Day" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-04", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2019-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2019-08-11", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-10-14", + "name": "Nyerere Day" + }, + { + "date": "2019-11-09", + "name": "Maulid Day" + }, + { + "date": "2019-12-09", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2020-04-07", + "name": "Karume Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-04-26", + "name": "Union Day" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2020-07-31", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2020-10-14", + "name": "Nyerere Day" + }, + { + "date": "2020-10-29", + "name": "Maulid Day" + }, + { + "date": "2020-12-09", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-02", + "name": "Zanzibar Revolution Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-07", + "name": "Karume Day" + }, + { + "date": "2021-04-26", + "name": "Union Day" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-07-07", + "name": "Saba Saba Day" + }, + { + "date": "2021-07-20", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-08-08", + "name": "Nane Nane Day" + }, + { + "date": "2021-10-14", + "name": "Nyerere Day" + }, + { + "date": "2021-10-18", + "name": "Maulid Day" + }, + { + "date": "2021-12-09", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Christmas Day" + } + ] + }, + "UA": { + "name": "Україна", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Новий Рік" + }, + { + "date": "2018-01-02", + "name": "Новий Рік" + }, + { + "date": "2018-01-07", + "name": "Різдво" + }, + { + "date": "2018-01-08", + "name": "Різдво (замінити день)" + }, + { + "date": "2018-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2018-04-08", + "name": "Великдень" + }, + { + "date": "2018-04-09", + "name": "Великдень" + }, + { + "date": "2018-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2018-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2018-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2018-05-27", + "name": "Трійця" + }, + { + "date": "2018-05-28", + "name": "Трійця" + }, + { + "date": "2018-06-28", + "name": "День Конституції" + }, + { + "date": "2018-08-24", + "name": "День Незалежності" + }, + { + "date": "2018-10-14", + "name": "День захисника України" + }, + { + "date": "2018-10-15", + "name": "День захисника України (замінити день)" + }, + { + "date": "2019-01-01", + "name": "Новий Рік" + }, + { + "date": "2019-01-02", + "name": "Новий Рік" + }, + { + "date": "2019-01-07", + "name": "Різдво" + }, + { + "date": "2019-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2019-04-28", + "name": "Великдень" + }, + { + "date": "2019-04-29", + "name": "Великдень" + }, + { + "date": "2019-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2019-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2019-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2019-06-16", + "name": "Трійця" + }, + { + "date": "2019-06-17", + "name": "Трійця" + }, + { + "date": "2019-06-28", + "name": "День Конституції" + }, + { + "date": "2019-08-24", + "name": "День Незалежності" + }, + { + "date": "2019-08-26", + "name": "День Незалежності (замінити день)" + }, + { + "date": "2019-10-14", + "name": "День захисника України" + }, + { + "date": "2020-01-01", + "name": "Новий Рік" + }, + { + "date": "2020-01-02", + "name": "Новий Рік" + }, + { + "date": "2020-01-07", + "name": "Різдво" + }, + { + "date": "2020-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2020-03-09", + "name": "Міжнародний жіночий день" + }, + { + "date": "2020-04-19", + "name": "Великдень" + }, + { + "date": "2020-04-20", + "name": "Великдень" + }, + { + "date": "2020-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2020-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2020-05-04", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2020-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2020-05-11", + "name": "День перемоги над нацизмом у Другій світовій війні (замінити день)" + }, + { + "date": "2020-06-07", + "name": "Трійця" + }, + { + "date": "2020-06-08", + "name": "Трійця" + }, + { + "date": "2020-06-28", + "name": "День Конституції" + }, + { + "date": "2020-06-29", + "name": "День Конституції (замінити день)" + }, + { + "date": "2020-08-24", + "name": "День Незалежності" + }, + { + "date": "2020-10-14", + "name": "День захисника України" + }, + { + "date": "2021-01-01", + "name": "Новий Рік" + }, + { + "date": "2021-01-02", + "name": "Новий Рік" + }, + { + "date": "2021-01-04", + "name": "Новий Рік (замінити день)" + }, + { + "date": "2021-01-07", + "name": "Різдво" + }, + { + "date": "2021-03-08", + "name": "Міжнародний жіночий день" + }, + { + "date": "2021-05-01", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2021-05-02", + "name": "День міжнародної солідарності трудящих" + }, + { + "date": "2021-05-02", + "name": "Великдень" + }, + { + "date": "2021-05-03", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2021-05-03", + "name": "Великдень" + }, + { + "date": "2021-05-04", + "name": "День міжнародної солідарності трудящих (замінити день)" + }, + { + "date": "2021-05-09", + "name": "День перемоги над нацизмом у Другій світовій війні" + }, + { + "date": "2021-05-10", + "name": "День перемоги над нацизмом у Другій світовій війні (замінити день)" + }, + { + "date": "2021-06-20", + "name": "Трійця" + }, + { + "date": "2021-06-21", + "name": "Трійця" + }, + { + "date": "2021-06-28", + "name": "День Конституції" + }, + { + "date": "2021-08-24", + "name": "День Незалежності" + }, + { + "date": "2021-10-14", + "name": "День захисника України" + } + ] + }, + "UG": { + "name": "Uganda", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-26", + "name": "Liberation Day" + }, + { + "date": "2018-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2018-03-08", + "name": "International Women's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-01", + "name": "Easter Sunday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-06-03", + "name": "Martyr's Day" + }, + { + "date": "2018-06-09", + "name": "National Heroes Day" + }, + { + "date": "2018-06-15", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2018-08-21", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2018-10-09", + "name": "Independence Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-26", + "name": "Liberation Day" + }, + { + "date": "2019-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2019-03-08", + "name": "International Women's Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-21", + "name": "Easter Sunday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-06-03", + "name": "Martyr's Day" + }, + { + "date": "2019-06-04", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2019-06-09", + "name": "National Heroes Day" + }, + { + "date": "2019-08-11", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2019-10-09", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Boxing Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-26", + "name": "Liberation Day" + }, + { + "date": "2020-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2020-03-08", + "name": "International Women's Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-12", + "name": "Easter Sunday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-24", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2020-06-03", + "name": "Martyr's Day" + }, + { + "date": "2020-06-09", + "name": "National Heroes Day" + }, + { + "date": "2020-07-31", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2020-10-09", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-26", + "name": "Liberation Day" + }, + { + "date": "2021-02-16", + "name": "Archbishop Janan Luwum Day" + }, + { + "date": "2021-03-08", + "name": "International Women's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-04", + "name": "Easter Sunday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-13", + "name": "End of Ramadan (Eid al-Fitr)" + }, + { + "date": "2021-06-03", + "name": "Martyr's Day" + }, + { + "date": "2021-06-09", + "name": "National Heroes Day" + }, + { + "date": "2021-07-20", + "name": "Feast of the Sacrifice (Eid al-Adha)" + }, + { + "date": "2021-10-09", + "name": "Independence Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "US": { + "name": "United States of America", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-01-15", + "name": "Martin Luther King Day" + }, + { + "date": "2018-02-19", + "name": "Washington’s Birthday" + }, + { + "date": "2018-05-28", + "name": "Memorial Day" + }, + { + "date": "2018-07-04", + "name": "Independence Day" + }, + { + "date": "2018-09-03", + "name": "Labor Day" + }, + { + "date": "2018-10-08", + "name": "Columbus Day" + }, + { + "date": "2018-11-11", + "name": "Veterans Day" + }, + { + "date": "2018-11-22", + "name": "Thanksgiving Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-01-21", + "name": "Martin Luther King Day" + }, + { + "date": "2019-02-18", + "name": "Washington’s Birthday" + }, + { + "date": "2019-05-27", + "name": "Memorial Day" + }, + { + "date": "2019-07-04", + "name": "Independence Day" + }, + { + "date": "2019-09-02", + "name": "Labor Day" + }, + { + "date": "2019-10-14", + "name": "Columbus Day" + }, + { + "date": "2019-11-11", + "name": "Veterans Day" + }, + { + "date": "2019-11-28", + "name": "Thanksgiving Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-01-20", + "name": "Martin Luther King Day" + }, + { + "date": "2020-02-17", + "name": "Washington’s Birthday" + }, + { + "date": "2020-05-25", + "name": "Memorial Day" + }, + { + "date": "2020-07-03", + "name": "Independence Day (substitute day)" + }, + { + "date": "2020-07-04", + "name": "Independence Day" + }, + { + "date": "2020-09-07", + "name": "Labor Day" + }, + { + "date": "2020-10-12", + "name": "Columbus Day" + }, + { + "date": "2020-11-11", + "name": "Veterans Day" + }, + { + "date": "2020-11-26", + "name": "Thanksgiving Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-01-18", + "name": "Martin Luther King Day" + }, + { + "date": "2021-02-15", + "name": "Washington’s Birthday" + }, + { + "date": "2021-05-31", + "name": "Memorial Day" + }, + { + "date": "2021-07-04", + "name": "Independence Day" + }, + { + "date": "2021-07-05", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-09-06", + "name": "Labor Day" + }, + { + "date": "2021-10-11", + "name": "Columbus Day" + }, + { + "date": "2021-11-11", + "name": "Veterans Day" + }, + { + "date": "2021-11-25", + "name": "Thanksgiving Day" + }, + { + "date": "2021-12-24", + "name": "Christmas Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-31", + "name": "New Year's Day (substitute day)" + } + ] + }, + "UY": { + "name": "Uruguay", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2018-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2019-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2020-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-07-18", + "name": "Día de la Constitución" + }, + { + "date": "2021-08-25", + "name": "Día de la Independencia" + }, + { + "date": "2021-12-25", + "name": "Navidad" + } + ] + }, + "VA": { + "name": "Stato della Città del Vaticano", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2018-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2018-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2018-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2018-03-19", + "name": "San Giuseppe" + }, + { + "date": "2018-04-02", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2018-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2018-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2018-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2018-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2018-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2018-11-01", + "name": "Ognissanti" + }, + { + "date": "2018-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2018-12-25", + "name": "Natale" + }, + { + "date": "2018-12-26", + "name": "Santo Stefano" + }, + { + "date": "2019-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2019-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2019-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2019-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2019-03-19", + "name": "San Giuseppe" + }, + { + "date": "2019-04-22", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2019-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2019-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2019-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2019-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2019-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2019-11-01", + "name": "Ognissanti" + }, + { + "date": "2019-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2019-12-25", + "name": "Natale" + }, + { + "date": "2019-12-26", + "name": "Santo Stefano" + }, + { + "date": "2020-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2020-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2020-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2020-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2020-03-19", + "name": "San Giuseppe" + }, + { + "date": "2020-04-13", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2020-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2020-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2020-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2020-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2020-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2020-11-01", + "name": "Ognissanti" + }, + { + "date": "2020-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2020-12-25", + "name": "Natale" + }, + { + "date": "2020-12-26", + "name": "Santo Stefano" + }, + { + "date": "2021-01-01", + "name": "Maria Santissima Madre di Dio" + }, + { + "date": "2021-01-06", + "name": "Epifania del Signore" + }, + { + "date": "2021-02-11", + "name": "Anniversario della istituzione dello Stato della Città del Vaticano" + }, + { + "date": "2021-03-13", + "name": "Anniversario dell'Elezione del Santo Padre" + }, + { + "date": "2021-03-19", + "name": "San Giuseppe" + }, + { + "date": "2021-04-05", + "name": "Lunedì dell’Angelo" + }, + { + "date": "2021-04-23", + "name": "San Giorgio - Onomastico del Santo Padre" + }, + { + "date": "2021-05-01", + "name": "San Giuseppe lavoratore" + }, + { + "date": "2021-06-29", + "name": "Santi Pietro e Paolo" + }, + { + "date": "2021-08-15", + "name": "Assunzione di Maria in Cielo" + }, + { + "date": "2021-09-08", + "name": "Festa della natività della madonna" + }, + { + "date": "2021-11-01", + "name": "Ognissanti" + }, + { + "date": "2021-12-08", + "name": "Immacolata Concezione" + }, + { + "date": "2021-12-25", + "name": "Natale" + }, + { + "date": "2021-12-26", + "name": "Santo Stefano" + } + ] + }, + "VE": { + "name": "Venezuela", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Año Nuevo" + }, + { + "date": "2018-02-12", + "name": "Carnaval" + }, + { + "date": "2018-02-13", + "name": "Carnaval" + }, + { + "date": "2018-03-29", + "name": "Jueves Santo" + }, + { + "date": "2018-03-30", + "name": "Viernes Santo" + }, + { + "date": "2018-04-01", + "name": "Pascua" + }, + { + "date": "2018-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2018-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2018-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2018-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2018-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2018-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2018-12-24", + "name": "Nochebuena" + }, + { + "date": "2018-12-25", + "name": "Navidad" + }, + { + "date": "2018-12-31", + "name": "Fin del Año" + }, + { + "date": "2019-01-01", + "name": "Año Nuevo" + }, + { + "date": "2019-03-04", + "name": "Carnaval" + }, + { + "date": "2019-03-05", + "name": "Carnaval" + }, + { + "date": "2019-04-18", + "name": "Jueves Santo" + }, + { + "date": "2019-04-19", + "name": "Viernes Santo" + }, + { + "date": "2019-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2019-04-21", + "name": "Pascua" + }, + { + "date": "2019-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2019-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2019-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2019-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2019-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2019-12-24", + "name": "Nochebuena" + }, + { + "date": "2019-12-25", + "name": "Navidad" + }, + { + "date": "2019-12-31", + "name": "Fin del Año" + }, + { + "date": "2020-01-01", + "name": "Año Nuevo" + }, + { + "date": "2020-02-24", + "name": "Carnaval" + }, + { + "date": "2020-02-25", + "name": "Carnaval" + }, + { + "date": "2020-04-09", + "name": "Jueves Santo" + }, + { + "date": "2020-04-10", + "name": "Viernes Santo" + }, + { + "date": "2020-04-12", + "name": "Pascua" + }, + { + "date": "2020-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2020-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2020-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2020-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2020-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2020-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2020-12-24", + "name": "Nochebuena" + }, + { + "date": "2020-12-25", + "name": "Navidad" + }, + { + "date": "2020-12-31", + "name": "Fin del Año" + }, + { + "date": "2021-01-01", + "name": "Año Nuevo" + }, + { + "date": "2021-02-15", + "name": "Carnaval" + }, + { + "date": "2021-02-16", + "name": "Carnaval" + }, + { + "date": "2021-04-01", + "name": "Jueves Santo" + }, + { + "date": "2021-04-02", + "name": "Viernes Santo" + }, + { + "date": "2021-04-04", + "name": "Pascua" + }, + { + "date": "2021-04-19", + "name": "Declaración de la Independencia" + }, + { + "date": "2021-05-01", + "name": "Fiesta del trabajo" + }, + { + "date": "2021-06-24", + "name": "Aniversario de la Batalla de Carabobo" + }, + { + "date": "2021-07-05", + "name": "Día de la Independencia" + }, + { + "date": "2021-07-24", + "name": "Natalicio de Simón Bolívar" + }, + { + "date": "2021-10-12", + "name": "Día de la resistencia indígena" + }, + { + "date": "2021-12-24", + "name": "Nochebuena" + }, + { + "date": "2021-12-25", + "name": "Navidad" + }, + { + "date": "2021-12-31", + "name": "Fin del Año" + } + ] + }, + "VN": { + "name": "Cộng hòa Xã hội chủ nghĩa Việt Nam", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2018-02-15", + "name": "Tết ngày lễ" + }, + { + "date": "2018-02-16", + "name": "Tết" + }, + { + "date": "2018-04-25", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2018-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2018-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2018-09-02", + "name": "Quốc khánh" + }, + { + "date": "2019-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2019-02-04", + "name": "Tết ngày lễ" + }, + { + "date": "2019-02-05", + "name": "Tết" + }, + { + "date": "2019-04-14", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2019-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2019-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2019-09-02", + "name": "Quốc khánh" + }, + { + "date": "2020-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2020-01-24", + "name": "Tết ngày lễ" + }, + { + "date": "2020-01-25", + "name": "Tết" + }, + { + "date": "2020-04-02", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2020-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2020-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2020-09-02", + "name": "Quốc khánh" + }, + { + "date": "2021-01-01", + "name": "Tết dương lịch" + }, + { + "date": "2021-02-11", + "name": "Tết ngày lễ" + }, + { + "date": "2021-02-12", + "name": "Tết" + }, + { + "date": "2021-04-21", + "name": "Giỗ tổ Hùng Vương" + }, + { + "date": "2021-04-30", + "name": "Ngày Giải phóng miền Nam" + }, + { + "date": "2021-05-01", + "name": "Ngày Quốc tế Lao động" + }, + { + "date": "2021-09-02", + "name": "Quốc khánh" + } + ] + }, + "XK": { + "name": "Republika e Kosovës", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Viti i Ri" + }, + { + "date": "2018-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2018-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2018-04-01", + "name": "Pashkët Katolike" + }, + { + "date": "2018-04-08", + "name": "Pashkët Ortodokse" + }, + { + "date": "2018-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2018-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2018-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2018-06-15", + "name": "Fitër Bajrami" + }, + { + "date": "2018-08-21", + "name": "Kurban Bajrami" + }, + { + "date": "2018-12-25", + "name": "Krishtlindja" + }, + { + "date": "2019-01-01", + "name": "Viti i Ri" + }, + { + "date": "2019-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2019-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2019-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2019-04-21", + "name": "Pashkët Katolike" + }, + { + "date": "2019-04-28", + "name": "Pashkët Ortodokse" + }, + { + "date": "2019-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2019-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2019-06-04", + "name": "Fitër Bajrami" + }, + { + "date": "2019-08-11", + "name": "Kurban Bajrami" + }, + { + "date": "2019-12-25", + "name": "Krishtlindja" + }, + { + "date": "2020-01-01", + "name": "Viti i Ri" + }, + { + "date": "2020-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2020-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2020-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2020-04-12", + "name": "Pashkët Katolike" + }, + { + "date": "2020-04-19", + "name": "Pashkët Ortodokse" + }, + { + "date": "2020-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2020-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2020-05-24", + "name": "Fitër Bajrami" + }, + { + "date": "2020-07-31", + "name": "Kurban Bajrami" + }, + { + "date": "2020-12-25", + "name": "Krishtlindja" + }, + { + "date": "2021-01-01", + "name": "Viti i Ri" + }, + { + "date": "2021-01-07", + "name": "Krishtlindjet Ortodokse" + }, + { + "date": "2021-02-17", + "name": "Dita e Pavarësisë" + }, + { + "date": "2021-04-04", + "name": "Pashkët Katolike" + }, + { + "date": "2021-04-09", + "name": "Dita e Kushtetutës" + }, + { + "date": "2021-05-01", + "name": "Dita Ndërkombëtare e Punonjësve" + }, + { + "date": "2021-05-02", + "name": "Pashkët Ortodokse" + }, + { + "date": "2021-05-09", + "name": "Dita e Evropës" + }, + { + "date": "2021-05-13", + "name": "Fitër Bajrami" + }, + { + "date": "2021-07-20", + "name": "Kurban Bajrami" + }, + { + "date": "2021-12-25", + "name": "Krishtlindja" } ] }, - "DE" : { - "name" : "Germany", - "bank_holidays" : [ + "YT": { + "name": "Mayotte", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "Nouvel An" + }, + { + "date": "2018-04-02", + "name": "Lundi de Pâques" + }, + { + "date": "2018-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2018-05-01", + "name": "Fête du travail" + }, + { + "date": "2018-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2018-05-10", + "name": "Ascension" + }, + { + "date": "2018-05-20", + "name": "Pentecôte" + }, + { + "date": "2018-05-21", + "name": "Lundi de Pentecôte" + }, + { + "date": "2018-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2018-08-15", + "name": "Assomption" + }, + { + "date": "2018-11-01", + "name": "Toussaint" + }, + { + "date": "2018-11-11", + "name": "Armistice 1918" + }, + { + "date": "2018-12-25", + "name": "Noël" + }, + { + "date": "2019-01-01", + "name": "Nouvel An" + }, + { + "date": "2019-04-22", + "name": "Lundi de Pâques" + }, + { + "date": "2019-04-27", + "name": "Abolition de l’esclavage" + }, + { + "date": "2019-05-01", + "name": "Fête du travail" + }, + { + "date": "2019-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2019-05-30", + "name": "Ascension" + }, + { + "date": "2019-06-09", + "name": "Pentecôte" + }, + { + "date": "2019-06-10", + "name": "Lundi de Pentecôte" + }, + { + "date": "2019-07-14", + "name": "Fête Nationale de la France" + }, { - "name" : "New Year Day", - "date" : "2016-01-01" + "date": "2019-08-15", + "name": "Assomption" }, { - "name" : "Good Friday", - "date" : "2016-03-25" + "date": "2019-11-01", + "name": "Toussaint" }, { - "name" : "Easter Monday", - "date" : "2016-03-28" + "date": "2019-11-11", + "name": "Armistice 1918" }, { - "name" : "Labour Day", - "date" : "2016-05-01" + "date": "2019-12-25", + "name": "Noël" }, { - "name" : "Ascension", - "date" : "2016-05-05" + "date": "2020-01-01", + "name": "Nouvel An" }, { - "name" : "Whit Monday", - "date" : "2016-05-16" + "date": "2020-04-13", + "name": "Lundi de Pâques" }, { - "name" : "Day of German Unity", - "date" : "2016-10-03" + "date": "2020-04-27", + "name": "Abolition de l’esclavage" }, { - "name" : "Christmas Day", - "date" : "2016-12-26" + "date": "2020-05-01", + "name": "Fête du travail" }, { - "name" : "St. Stephens Day", - "date" : "2016-12-27" - } - ] - }, - "IS" : { - "name" : "Iceland" - }, - "ES" : { - "name" : "Spain", - "bank_holidays" : [ + "date": "2020-05-08", + "name": "Fête de la Victoire 1945" + }, + { + "date": "2020-05-21", + "name": "Ascension" + }, + { + "date": "2020-05-31", + "name": "Pentecôte" + }, + { + "date": "2020-06-01", + "name": "Lundi de Pentecôte" + }, + { + "date": "2020-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2020-08-15", + "name": "Assomption" + }, { - "name" : "New Year Day", - "date" : "2016-01-01" + "date": "2020-11-01", + "name": "Toussaint" }, { - "name" : "Epiphany", - "date" : "2016-01-06" + "date": "2020-11-11", + "name": "Armistice 1918" }, { - "name" : "Good Friday", - "date" : "2016-03-25" + "date": "2020-12-25", + "name": "Noël" }, { - "name" : "Labour Day", - "date" : "2016-05-01" + "date": "2021-01-01", + "name": "Nouvel An" }, { - "name" : "Assumption Day", - "date" : "2016-08-15" + "date": "2021-04-05", + "name": "Lundi de Pâques" }, { - "name" : "Hispanic Day", - "date" : "2016-10-12" + "date": "2021-04-27", + "name": "Abolition de l’esclavage" }, { - "name" : "All Saints Day", - "date" : "2016-11-01" + "date": "2021-05-01", + "name": "Fête du travail" }, { - "name" : "Constitution Day", - "date" : "2016-12-06" + "date": "2021-05-08", + "name": "Fête de la Victoire 1945" }, { - "name" : "Immaculate Conception Day", - "date" : "2016-12-08" + "date": "2021-05-13", + "name": "Ascension" }, { - "name" : "Christmas Day", - "date" : "2016-12-25" + "date": "2021-05-23", + "name": "Pentecôte" + }, + { + "date": "2021-05-24", + "name": "Lundi de Pentecôte" + }, + { + "date": "2021-07-14", + "name": "Fête Nationale de la France" + }, + { + "date": "2021-08-15", + "name": "Assomption" + }, + { + "date": "2021-11-01", + "name": "Toussaint" + }, + { + "date": "2021-11-11", + "name": "Armistice 1918" + }, + { + "date": "2021-12-25", + "name": "Noël" } ] }, - "UK" : { - "name" : "United Kingdom", - "default" : 1, - "bank_holidays" : [ + "ZA": { + "name": "South Africa", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-21", + "name": "Human Rights Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Family Day" + }, + { + "date": "2018-04-27", + "name": "Freedom Day" + }, + { + "date": "2018-05-01", + "name": "Workers' Day" + }, + { + "date": "2018-06-16", + "name": "Youth Day" + }, + { + "date": "2018-08-09", + "name": "National Women's Day" + }, + { + "date": "2018-09-24", + "name": "Heritage Day" + }, + { + "date": "2018-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2018-12-17", + "name": "Public Holiday" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-21", + "name": "Human Rights Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Family Day" + }, + { + "date": "2019-04-27", + "name": "Freedom Day" + }, { - "name" : "New Year Day", - "date" : "2016-01-01" + "date": "2019-05-01", + "name": "Workers' Day" }, { - "name" : "Good Friday", - "date" : "2016-03-25" + "date": "2019-06-16", + "name": "Youth Day" }, { - "name" : "Easter Monday", - "date" : "2016-03-28" + "date": "2019-06-17", + "name": "Public Holiday" }, { - "name" : "Early May Bank Holiday", - "date" : "2016-05-02" + "date": "2019-08-09", + "name": "National Women's Day" }, { - "name" : "Spring Bank Holiday", - "date" : "2016-05-30" + "date": "2019-09-24", + "name": "Heritage Day" }, { - "name" : "Boxing Day", - "date" : "2016-12-26" + "date": "2019-12-16", + "name": "Day of Reconciliation" }, { - "name" : "Christmas Day", - "date" : "2016-12-27" + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-21", + "name": "Human Rights Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Family Day" + }, + { + "date": "2020-04-27", + "name": "Freedom Day" + }, + { + "date": "2020-05-01", + "name": "Workers' Day" + }, + { + "date": "2020-06-16", + "name": "Youth Day" + }, + { + "date": "2020-08-09", + "name": "National Women's Day" + }, + { + "date": "2020-08-10", + "name": "Public Holiday" + }, + { + "date": "2020-09-24", + "name": "Heritage Day" + }, + { + "date": "2020-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-21", + "name": "Human Rights Day" + }, + { + "date": "2021-03-22", + "name": "Public Holiday" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Family Day" + }, + { + "date": "2021-04-27", + "name": "Freedom Day" + }, + { + "date": "2021-05-01", + "name": "Workers' Day" + }, + { + "date": "2021-06-16", + "name": "Youth Day" + }, + { + "date": "2021-08-09", + "name": "National Women's Day" + }, + { + "date": "2021-09-24", + "name": "Heritage Day" + }, + { + "date": "2021-12-16", + "name": "Day of Reconciliation" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Day of Goodwill" + }, + { + "date": "2021-12-27", + "name": "Public Holiday" } ] }, - "US" : { - "name" : "United States", - "bank_holidays" : [ + "ZM": { + "name": "Zambia", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-09", + "name": "Women’s Day" + }, + { + "date": "2018-03-12", + "name": "Youth Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, { - "name" : "New Year Day", - "date" : "2016-01-01" + "date": "2018-04-02", + "name": "Easter Monday" }, { - "name" : "Martin Luther King Day", - "date" : "2016-01-18" + "date": "2018-05-01", + "name": "Labour Day" }, { - "name" : "Memorial Day", - "date" : "2016-05-30" + "date": "2018-05-25", + "name": "African Freedom Day" }, { - "name" : "Independence Day", - "date" : "2016-07-04" + "date": "2018-07-02", + "name": "Heroes' Day" }, { - "name" : "Labor Day", - "date" : "2016-09-05" + "date": "2018-07-03", + "name": "Unity Day" }, { - "name" : "Veterans Day", - "date" : "2016-11-11" + "date": "2018-08-06", + "name": "Farmers' Day" }, { - "name" : "Thanksgiving", - "date" : "2016-11-24" + "date": "2018-10-24", + "name": "Independence Day" }, { - "name" : "Christmas Day", - "date" : "2016-12-26" + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2019-01-01", + "name": "New Year's Day" + }, + { + "date": "2019-03-09", + "name": "Women’s Day" + }, + { + "date": "2019-03-12", + "name": "Youth Day" + }, + { + "date": "2019-04-19", + "name": "Good Friday" + }, + { + "date": "2019-04-22", + "name": "Easter Monday" + }, + { + "date": "2019-05-01", + "name": "Labour Day" + }, + { + "date": "2019-05-25", + "name": "African Freedom Day" + }, + { + "date": "2019-07-01", + "name": "Heroes' Day" + }, + { + "date": "2019-07-02", + "name": "Unity Day" + }, + { + "date": "2019-08-05", + "name": "Farmers' Day" + }, + { + "date": "2019-10-24", + "name": "Independence Day" + }, + { + "date": "2019-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-01-01", + "name": "New Year's Day" + }, + { + "date": "2020-03-09", + "name": "Women’s Day" + }, + { + "date": "2020-03-12", + "name": "Youth Day" + }, + { + "date": "2020-04-10", + "name": "Good Friday" + }, + { + "date": "2020-04-13", + "name": "Easter Monday" + }, + { + "date": "2020-05-01", + "name": "Labour Day" + }, + { + "date": "2020-05-25", + "name": "African Freedom Day" + }, + { + "date": "2020-07-06", + "name": "Heroes' Day" + }, + { + "date": "2020-07-07", + "name": "Unity Day" + }, + { + "date": "2020-08-03", + "name": "Farmers' Day" + }, + { + "date": "2020-10-24", + "name": "Independence Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-03-09", + "name": "Women’s Day" + }, + { + "date": "2021-03-12", + "name": "Youth Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-25", + "name": "African Freedom Day" + }, + { + "date": "2021-07-05", + "name": "Heroes' Day" + }, + { + "date": "2021-07-06", + "name": "Unity Day" + }, + { + "date": "2021-08-02", + "name": "Farmers' Day" + }, + { + "date": "2021-10-24", + "name": "Independence Day" + }, + { + "date": "2021-10-25", + "name": "Independence Day (substitute day)" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" } ] }, - "CZ" : { - "name" : "Czech Republic", - "bank_holidays" : [ + "ZW": { + "name": "Zimbabwe", + "bank_holidays": [ + { + "date": "2018-01-01", + "name": "New Year's Day" + }, + { + "date": "2018-03-30", + "name": "Good Friday" + }, + { + "date": "2018-04-02", + "name": "Easter Monday" + }, + { + "date": "2018-04-18", + "name": "Independence Day" + }, + { + "date": "2018-05-01", + "name": "Labour Day" + }, + { + "date": "2018-05-25", + "name": "Africa Day" + }, + { + "date": "2018-08-13", + "name": "Heroes' Day" + }, + { + "date": "2018-08-14", + "name": "Defence Forces Day" + }, + { + "date": "2018-12-22", + "name": "Unity Day" + }, + { + "date": "2018-12-25", + "name": "Christmas Day" + }, + { + "date": "2018-12-26", + "name": "Boxing Day" + }, { - "name" : "Czech Statehood Day", - "date" : "2017-09-28" + "date": "2019-01-01", + "name": "New Year's Day" }, { - "name" : "Czechoslovak Republic Independence Day", - "date" : "2017-10-28" + "date": "2019-04-18", + "name": "Independence Day" }, { - "name" : "Freedom and Democracy Day", - "date" : "2017-11-17" + "date": "2019-04-19", + "name": "Good Friday" }, { - "name" : "Christmas Eve", - "date" : "2017-12-24" + "date": "2019-04-22", + "name": "Easter Monday" }, { - "name" : "Christmas Day", - "date" : "2017-12-25" + "date": "2019-05-01", + "name": "Labour Day" }, { - "name" : "Boxing Day", - "date" : "2017-12-26" + "date": "2019-05-25", + "name": "Africa Day" }, { - "name" : "Czech Republic Independence Day", - "date" : "2018-01-01" + "date": "2019-08-12", + "name": "Heroes' Day" }, { - "name" : "Good Friday", - "date" : "2018-03-30" + "date": "2019-08-13", + "name": "Defence Forces Day" }, { - "name" : "Easter Monday", - "date" : "2018-04-02" + "date": "2019-12-22", + "name": "Unity Day" }, { - "name" : "May Day", - "date" : "2018-05-01" + "date": "2019-12-25", + "name": "Christmas Day" }, { - "name" : "Victory Day", - "date" : "2018-05-08" + "date": "2019-12-26", + "name": "Boxing Day" }, { - "name" : "St. Cyril and St. Methodius Day", - "date" : "2018-07-05" + "date": "2020-01-01", + "name": "New Year's Day" }, { - "name" : "Jan Hus Day", - "date" : "2018-07-06" + "date": "2020-04-10", + "name": "Good Friday" }, { - "name" : "Czech Statehood Day", - "date" : "2018-09-28" + "date": "2020-04-13", + "name": "Easter Monday" }, { - "name" : "Czechoslovak Republic Independence Day", - "date" : "2018-10-28" + "date": "2020-04-18", + "name": "Independence Day" }, { - "name" : "Freedom and Democracy Day", - "date" : "2018-11-17" + "date": "2020-05-01", + "name": "Labour Day" }, { - "name" : "Christmas Eve", - "date" : "2018-12-24" + "date": "2020-05-25", + "name": "Africa Day" }, { - "name" : "Christmas Day", - "date" : "2018-12-25" + "date": "2020-08-10", + "name": "Heroes' Day" }, { - "name" : "Boxing Day", - "date" : "2018-12-26" + "date": "2020-08-11", + "name": "Defence Forces Day" + }, + { + "date": "2020-12-22", + "name": "Unity Day" + }, + { + "date": "2020-12-25", + "name": "Christmas Day" + }, + { + "date": "2020-12-26", + "name": "Boxing Day" + }, + { + "date": "2021-01-01", + "name": "New Year's Day" + }, + { + "date": "2021-04-02", + "name": "Good Friday" + }, + { + "date": "2021-04-05", + "name": "Easter Monday" + }, + { + "date": "2021-04-18", + "name": "Independence Day" + }, + { + "date": "2021-05-01", + "name": "Labour Day" + }, + { + "date": "2021-05-25", + "name": "Africa Day" + }, + { + "date": "2021-08-09", + "name": "Heroes' Day" + }, + { + "date": "2021-08-10", + "name": "Defence Forces Day" + }, + { + "date": "2021-12-22", + "name": "Unity Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + }, + { + "date": "2021-12-26", + "name": "Boxing Day" + } + ] + }, + "ZZ": { + "name": "Other", + "bank_holidays": [ + { + "name": "Early May bank holiday", + "date": "2015-05-04" } ] } diff --git a/config/timeoff-dev.code-workspace b/config/timeoff-dev.code-workspace new file mode 100644 index 000000000..2a0ed79b2 --- /dev/null +++ b/config/timeoff-dev.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": ".." + } + ] +} \ No newline at end of file diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml new file mode 100644 index 000000000..5dd90f9b8 --- /dev/null +++ b/docker-compose-dev.yaml @@ -0,0 +1,73 @@ +version: '3.8' +services: + web: + container_name: web + build: . + env_file: + - .env.dev + environment: + DB_HOST: ${DB_HOST} + DB_DATABASE: ${DB_DATABASE} + DB_USERNAME: ${DB_USERNAME} + DB_PASSWORD: ${DB_PASSWORD} + DB_DIALECT: ${DB_DIALECT} + DATABASE_URL: ${DB_DIALECT}://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_DATABASE} + USE_SSL: ${USE_SSL} + DB_SSL_REQUIRE: ${DB_SSL_REQUIRE} + DB_SSL_REJECT_UNAUTHORIZED: ${DB_SSL_REJECT_UNAUTHORIZED} + OPTION_ALLOW_NEW_REGISTRATIONS: ${OPTION_ALLOW_NEW_REGISTRATIONS} + # SMTP_AUTH_PASS: ${SMTP_AUTH_PASS} + # SMTP_AUTH_USER: ${SMTP_AUTH_USER} + # SMTP_FROM: ${SMTP_FROM} + SMTP_HOST: ${SMTP_HOST} + # SMTP_PORT: ${SMTP_PORT} + # SMTP_REQUIRE_TLS: ${SMTP_REQUIRE_TLS} + ports: + - "3000:3000" + # volumes: + # - ./config:/app/config + restart: unless-stopped + + postgres: + image: postgres:latest + container_name: postgres + env_file: + - .env.dev + environment: + POSTGRES_DB: ${DB_DATABASE} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - dev-pgsql:/var/lib/postgresql/data + ports: + - "${DB_PORT}:5432" + + # mysql: + # image: mysql:latest + # container_name: mysql + # environment: + # MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + # MYSQL_DATABASE: ${DB_DATABASE} + # MYSQL_USER: ${DB_USER} + # MYSQL_PASSWORD: ${DB_PASSWORD} + # MYSQL_ROOT_HOST: '%' + # volumes: + # - my-mysql-data:/var/lib/mysql + # ports: + # - "${DB_PORT}:3306" + # command: --default-authentication-plugin=mysql_native_password + + # admin: + # image: phpmyadmin/phpmyadmin + # container_name: admin + # environment: + # PMA_HOST: ${DB_HOST} + # PMA_PORT: ${DB_PORT} + # ports: + # - "8080:80" + # depends_on: + # - mysql + +volumes: +# my-mysql-data: + dev-pgsql: diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 000000000..82b9c4d36 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,73 @@ +version: '3.8' +services: + web: + container_name: web + build: . + environment: + DATABASE_URL: ${DATABASE_URL} + USE_SSL: ${USE_SSL} + OPTION_ALLOW_NEW_REGISTRATIONS: ${OPTION_ALLOW_NEW_REGISTRATIONS} + SMTP_AUTH_PASS: ${SMTP_AUTH_PASS} + SMTP_AUTH_USER: ${SMTP_AUTH_USER} + SMTP_FROM: ${SMTP_FROM} + SMTP_HOST: ${SMTP_HOST} + SMTP_PORT: ${SMTP_PORT} + SMTP_REQUIRE_TLS: ${SMTP_REQUIRE_TLS} + ports: + - "${APP_PORT}:${APP_PORT}" + # volumes: + # - ./config:/app/config + restart: unless-stopped + + # postgres: + # image: postgres:latest + # container_name: postgres + # environment: + # POSTGRES_DB: ${DB_DATABASE} + # POSTGRES_USER: ${DB_USER} + # POSTGRES_PASSWORD: ${DB_PASSWORD} + # volumes: + # - my-postgres-data:/var/lib/postgresql/data + # ports: + # - "5432:5432" + + # mysql: + # image: mysql:latest + # container_name: mysql + # environment: + # MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS} + # MYSQL_DATABASE: ${DB_DATABASE} + # MYSQL_USER: ${DB_USER} + # MYSQL_PASSWORD: ${DB_PASSWORD} + # MYSQL_ROOT_HOST: '%' + # volumes: + # - my-mysql-data:/var/lib/mysql + # ports: + # - "${DB_PORT}:3306" + # command: --default-authentication-plugin=mysql_native_password + + # admin: + # image: phpmyadmin/phpmyadmin + # container_name: admin + # environment: + # PMA_HOST: ${DB_HOST} + # PMA_PORT: ${DB_PORT} + # ports: + # - "8080:80" + # depends_on: + # - mysql + + # adminer: + # image: adminer + # restart: always + # ports: + # - "8080:8080" + # environment: + # ADMINER_DEFAULT_SERVER: ${DB_HOST} + # ADMINER_DEFAULT_DB: ${DB_NAME} + # ADMINER_DEFAULT_USER: ${DB_USER} + # ADMINER_DEFAULT_PASSWORD: ${DB_PASSWORD} + +volumes: + # my-mysql-data: + my-postgres-data: diff --git a/docs/SessionStoreInRedis.md b/docs/SessionStoreInRedis.md new file mode 100644 index 000000000..c019af7d3 --- /dev/null +++ b/docs/SessionStoreInRedis.md @@ -0,0 +1,15 @@ +# How to use Redis as storage for Sessions + +By default application uses its database as a storage for session data (`Sessions` table). + +It is possible to use different storage mechanism for Sessions data: [Redis](https://redis.io/). + +## Steps + +* Ensure the application's source is at least `1.4.0` +* Stop the application +* Open `config/app.json` for editing +* Update `sessions.store` section to be `redis` +* Update `sessions.redis`'s `host` and `port` pointing to corresponding instance of Redis +* Save the configuration file +* Restart the application diff --git a/docs/bootstrap_config.json b/docs/bootstrap_config.json index f9368e601..88ff907ee 100755 --- a/docs/bootstrap_config.json +++ b/docs/bootstrap_config.json @@ -432,4 +432,4 @@ "transition.js" ], "customizerUrl": "http://getbootstrap.com/customize/?id=2f747106c336fe71d867" -} \ No newline at end of file +} diff --git a/docs/db_design.txt b/docs/db_design.txt index 37c880696..23895cbd8 100644 --- a/docs/db_design.txt +++ b/docs/db_design.txt @@ -47,11 +47,11 @@ To have nice visualisation of database schema use this tool http://ondras.zarovi VARBINARY NULL - + INTEGER NULL - + INTEGER NULL @@ -71,6 +71,9 @@ To have nice visualisation of database schema use this tool http://ondras.zarovi TINYINT NULL + +VARBINARY +'NULL' id @@ -112,7 +115,7 @@ To have nice visualisation of database schema use this tool http://ondras.zarovi INTEGER NULL - + INTEGER NULL @@ -124,7 +127,7 @@ To have nice visualisation of database schema use this tool http://ondras.zarovi INTEGER NULL - + INTEGER NULL @@ -142,7 +145,7 @@ To have nice visualisation of database schema use this tool http://ondras.zarovi INTEGER NULL - + INTEGER NULL diff --git a/docs/extend_colors_for_leave_type.md b/docs/extend_colors_for_leave_type.md new file mode 100644 index 000000000..76dcec6fe --- /dev/null +++ b/docs/extend_colors_for_leave_type.md @@ -0,0 +1,27 @@ + +# Leave types colour setup + +For some organizations the available set of colours is not enough. The page describes how to extend the set and amend the values of available colours. + +# Amend colours of available options + +## To change the predefined colours one needs: + +* to open `scss/main.scss` +* find classes of `leave_type_color_1` or similar +* updated the classes to have relevant color for the `background` property +* run the `npm run compile-sass` command to generate the css file +* comment changes to git. + +## To add new colours to the set of available colours + +* open handlebar partial file that holds available options for the colour picker: `views/partials/options_for_color_picker.hbs` +* add new option by adding line of following format: +``` +
  • Color X
  • +``` +* * where `X` is next integer from the biggest one available so far +* open `scss/main.scss` +* find classes of `leave_type_color_` shape and add new one `leave_type_color_X` with colour of your choice +* run the `npm run compile-sass` command to generate the css file +* comment changes to git. diff --git a/i18n.js b/i18n.js new file mode 100644 index 000000000..49a1feab1 --- /dev/null +++ b/i18n.js @@ -0,0 +1,15 @@ +const i18n = require('i18n') +const path = require('path') + +i18n.configure({ + locales: ['en', 'es'], // Add the languages you want to support + directory: path.join(__dirname, 'locales'), + defaultLocale: 'en', + cookie: 'lang', // Name of the cookie to store the language preference + queryParameter: 'lang', // Query parameter to change the language + autoReload: true, + syncFiles: true, + objectNotation: true +}) + +module.exports = i18n diff --git a/lib/config.js b/lib/config.js index d01d611de..d19cc13ed 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,11 +1,74 @@ +'use strict' -'use strict'; - -var nconf = require('nconf'); +const nconf = require('nconf') +const SEND_EMAIL = process.env.SEND_EMAIL || 'false' +const BRANDING_URL = process.env.BRANDING_URL || 'https://timeoff.management' +const BRANDING_WEBSITE = process.env.BRANDING_WEBSITE || 'https://timeoff.management' nconf .argv() - .file('localisation', { file: __dirname+'/../config/localisation.json' }) - .file({ file: __dirname+'/../config/app.json' }); + .env({ + separator: '_', + lowerCase: true, + parseValues: true, + transform: function(obj) { + if (obj.key === 'GOOGLE_AUTH_DOMAINS') { + obj.value = obj.value.split(',') + } + return obj + } + }) + .file('localisation', { file: __dirname + '/../config/localisation.json' }) + .file({ file: __dirname + '/../config/app.json' }) + .defaults({ + branding: { + url: BRANDING_URL, + website: BRANDING_WEBSITE + }, + crypto_secret: '!2~`HswpPPLa22+=±§sdq qwe,appp qwwokDF_', + login: { + default: true, + google: false + }, + send_email: SEND_EMAIL, + smtp: { + host: 'localhost', + port: 25, + from: 'email@test.com', + auth: { + user: '', + pass: '', + required: true + } + }, + sessions: { + secret: 'my dirty secret ;khjsdkjahsdajhasdam,nnsnad,', + store: 'sequelize', + redis: { + host: 'localhost', + port: 6379 + } + }, + google: { + analytics: { + tracker: '' + }, + auth: { + clientId: '', + clientSecret: '', + domains: [] + } + }, + slack: { + token: '', + icon_url: '', + bot_name: '' + }, + options: { + registration: false + }, + locale_code_for_sorting: 'en', + force_to_explicitly_select_type_when_requesting_new_leave: false + }) -module.exports = nconf; +module.exports = nconf diff --git a/lib/email.js b/lib/email.js index 44e0739b9..54c1709c1 100644 --- a/lib/email.js +++ b/lib/email.js @@ -1,28 +1,70 @@ +'use strict' + +const bluebird = require('bluebird') +const _hbs = require('handlebars') +const { + allowInsecurePrototypeAccess +} = require('@handlebars/allow-prototype-access') + +const handlebars = require('express-handlebars').create({ + partialsDir: __dirname + '/../views/partials/', + extname: '.hbs', + helpers: require('./view/helpers')(), + handlebars: allowInsecurePrototypeAccess(_hbs), + runtimeOptions: { + allowedProtoProperties: { + full_name: true, + name: true, + get_leave_type_name: true, + get_start_leave_day: true, + get_end_leave_day: true, + get_end_leave_day: true, + ldap_auth_enabled: true, + get_reset_password_token: true + }, + allowedProtoMethods: { + full_name: true, + name: true, + get_leave_type_name: true, + get_start_leave_day: true, + get_end_leave_day: true, + get_end_leave_day: true, + ldap_auth_enabled: true, + get_reset_password_token: true + } + } +}) -'use strict'; +const config = require('./config') +const nodemailer = require('nodemailer') +const smtpTransport = require('nodemailer-smtp-transport') +const model = require('./model/db') +const { getCommentsForLeave } = require('./model/comment') -var bluebird = require('bluebird'), -handlebars = require('express-handlebars').create({ - partialsDir : __dirname+'/../views/partials/', - extname : '.hbs', - helpers : require('./view/helpers')(), -}), -config = require('./config'), -nodemailer = require('nodemailer'), -smtpTransport = require('nodemailer-smtp-transport'); +function Email() {} -function Email(){ +const send_email = config.get('send_email') +console.log('get_send_email: ', config.get('send_email')) -}; +let smtpConfig = config.get('smtp') +if (config.get('smtp:auth:required')) { + delete smtpConfig.auth.required +} else { + delete smtpConfig.auth +} +const transporter = nodemailer.createTransport( + smtpTransport(smtpConfig) +) +console.log('SMTP Config:', smtpConfig) // This is a little helper that ensure that data in context are in a shape // suitable for usage in templates // function _promise_to_unfold_context(context) { - if (context.hasOwnProperty('user')){ - return context.user.reload_with_session_details(); + if (context.user) { + return context.user.reload_with_session_details() } else { - return bluebird.resolve(1); + return bluebird.resolve(1) } } @@ -32,443 +74,467 @@ function _promise_to_unfold_context(context) { // * render inner part of template // * place the innerpart ingo ready to use HTML wrapper -Email.prototype.promise_rendered_email_template = function(args){ - var filename = args.template_name, - context = args.context || {}; +Email.prototype.promise_rendered_email_template = async function(args) { + try { + const filename = args.template_name + const context = args.context || {} - return bluebird.resolve() + // Prepare context to be passed into first rendering stage + const unfoldedContext = await _promise_to_unfold_context(context) + // console.log('unfoldedContext:', unfoldedContext); - // Prepare context to be passed into first rendering stage - .then(function(){ - return _promise_to_unfold_context(context); - }) - - // Render inner part of email - .then(function(){ - return handlebars.render( - __dirname+'/../views/email/'+filename+'.hbs', + // Render inner part of email + const renderedEmail = await handlebars.render( + __dirname + '/../views/email/' + filename + '.hbs', context - ); - }) - - // Produce final (ready to use) version of email and Subject - .then(function(text){ + ) + // console.log('renderedEmail:', renderedEmail); // Extract subject from email - var subject_and_body = text.split(/\r?\n=====\r?\n/); - - return handlebars - // Render ready to use email: wrap the content with fancy HTML boilerplate - .render( - __dirname+'/../views/email/wrapper.hbs', - { - subject : subject_and_body[0], - body : subject_and_body[1], - } - ) - .then(function(final_email){ - return bluebird.resolve({ - subject : subject_and_body[0], - body : final_email, - }); - }); - }); - -}; + const subject_and_body = renderedEmail.split(/\r?\n=====\r?\n/) + console.log('subject_and_body:', subject_and_body) + + // Render ready to use email: wrap the content with fancy HTML boilerplate + const final_email = await handlebars.render( + __dirname + '/../views/email/wrapper.hbs', + { + subject: subject_and_body[0], + body: subject_and_body[1] + } + ) + // console.log('final_email:', final_email); + + return { + subject: subject_and_body[0], + body: final_email + } + } catch (error) { + console.error('Error in promise_rendered_email_template:', error) + } +} // Return function that support same interface as sendMail but promisified. // If current configuration does not allow sending emails, it return empty function // -Email.prototype.get_send_email = function(){ - +Email.prototype.get_send_email = function() { // Check if current installation is set to send emails - if (! config.get("send_emails") || ! config.get("email_transporter") ) { - return function(){ - console.log('Pretend to send email: '+ JSON.stringify(arguments)); - return bluebird.resolve(); - }; + if (send_email == 'false') { + return function() { + console.debug('Pretend to send email: ' + JSON.stringify(arguments)) + return bluebird.resolve() + } } - var transporter = nodemailer.createTransport(smtpTransport( - config.get("email_transporter") - )); - - var send_mail = bluebird.promisify(transporter.sendMail, transporter); - - return send_mail; -}; + // Send E-mail transactionnally + return function(data) { + return new Promise(function(resolve, reject) { + transporter.sendMail(data, (err, info) => { + if (err) { + console.error('Error while sending mail', err) + return reject(err) + } + resolve(info) + }) + }) + } +} // Send registration complete email for provided user // -Email.prototype.promise_registration_email = function(args){ - var self = this, - user = args.user; - var send_mail = self.get_send_email(); - - return self.promise_rendered_email_template({ - template_name : 'registration_complete', - context : {user : user} - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : user.email, - subject : email_obj.subject, - html : email_obj.body, +Email.prototype.promise_registration_email = function(args) { + const self = this + const user = args.user + const send_mail = self.get_send_email() + + return self + .promise_rendered_email_template({ + template_name: 'registration_complete', + context: { user } }) - .then(function(send_result){ - return user.record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); -}; - -Email.prototype.promise_add_new_user_email = function(args){ - var self = this, - company = args.company, - admin_user = args.admin_user, - new_user = args.new_user, - send_mail = self.get_send_email(); - - return self.promise_rendered_email_template({ - template_name : 'add_new_user', - context : { - new_user : new_user, - admin_user : admin_user, - company : company, - user : new_user, - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : new_user.email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return new_user.record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: user.email, + subject: email_obj.subject, + html: email_obj.body + }) + .then(send_result => bluebird.resolve(send_result)) + .finally(() => user.record_email_addressed_to_me(email_obj)) + ) +} -}; +Email.prototype.promise_add_new_user_email = function(args) { + const self = this + const company = args.company + const admin_user = args.admin_user + const new_user = args.new_user + const send_mail = self.get_send_email() + + return self + .promise_rendered_email_template({ + template_name: 'add_new_user', + context: { + new_user, + admin_user, + company, + user: new_user + } + }) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: new_user.email, + subject: email_obj.subject, + html: email_obj.body + }) + .then(send_result => bluebird.resolve(send_result)) + .finally(() => new_user.record_email_addressed_to_me(email_obj)) + ) +} -Email.prototype.promise_leave_request_revoke_emails = function(args){ - var self = this, - leave = args.leave, - send_mail = self.get_send_email(); +Email.prototype.promise_leave_request_revoke_emails = function(args) { + const self = this + const leave = args.leave + const send_mail = self.get_send_email() - var template_name_to_supervisor = 'leave_request_revoke_to_supervisor'; - var template_name_to_requestor = 'leave_request_revoke_to_requestor'; + let template_name_to_supervisor = 'leave_request_revoke_to_supervisor' + let template_name_to_requestor = 'leave_request_revoke_to_requestor' - if ( leave.get('user').is_auto_approve() ) { - template_name_to_supervisor = 'leave_request_revoke_to_supervisor_autoapprove'; - template_name_to_requestor = 'leave_request_revoke_to_requestor_autoapprove'; + if ( + model.Leave.does_skip_approval(leave.get('user'), leave.get('leave_type')) + ) { + template_name_to_supervisor = + 'leave_request_revoke_to_supervisor_autoapprove' + template_name_to_requestor = 'leave_request_revoke_to_requestor_autoapprove' } - var promise_email_to_supervisor = self.promise_rendered_email_template({ - template_name : template_name_to_supervisor, - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('approver'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('approver').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('approver').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - var promise_email_to_requestor = self.promise_rendered_email_template({ - template_name : template_name_to_requestor, - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('user'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('user').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('user').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - return bluebird.join( - promise_email_to_supervisor, promise_email_to_requestor, - function(){ - return bluebird.resolve(); - } - ); -}; + const promise_email_to_supervisor = comments => + self + .promise_rendered_email_template({ + template_name: template_name_to_supervisor, + context: { + leave, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver') + } + }) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: leave.get('approver').email, + subject: email_obj.subject, + html: email_obj.body + }).finally(() => + leave.get('approver').record_email_addressed_to_me(email_obj) + ) + ) -Email.prototype.promise_leave_request_emails = function(args){ - var self = this, - leave = args.leave, - send_mail = self.get_send_email(); + const promise_email_to_requestor = comments => + self + .promise_rendered_email_template({ + template_name: template_name_to_requestor, + context: { + leave, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('user') + } + }) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: leave.get('user').email, + subject: email_obj.subject, + html: email_obj.body + }).finally(() => + leave.get('user').record_email_addressed_to_me(email_obj) + ) + ) + + return getCommentsForLeave({ leave }).then(comments => + bluebird.join( + promise_email_to_supervisor(comments), + promise_email_to_requestor(comments), + () => bluebird.resolve() + ) + ) +} - var template_name_to_supervisor = 'leave_request_to_supervisor'; - var template_name_to_requestor = 'leave_request_to_requestor'; +Email.prototype.promise_leave_request_emails = function(args) { + const self = this + const leave = args.leave + const send_mail = self.get_send_email() - if ( leave.get('user').is_auto_approve() ) { - template_name_to_supervisor = 'leave_request_to_supervisor_autoapprove'; - template_name_to_requestor = 'leave_request_to_requestor_autoapprove'; + let template_name_to_supervisor = 'leave_request_to_supervisor' + let template_name_to_requestor = 'leave_request_to_requestor' + + if (leave.is_auto_approve()) { + template_name_to_supervisor = 'leave_request_to_supervisor_autoapprove' + template_name_to_requestor = 'leave_request_to_requestor_autoapprove' } - var promise_email_to_supervisor = self.promise_rendered_email_template({ - template_name : template_name_to_supervisor, - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('approver'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('approver').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('approver').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - var promise_email_to_requestor = self.promise_rendered_email_template({ - template_name : template_name_to_requestor, - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('approver'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('user').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('user').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - return bluebird.join( - promise_email_to_supervisor, promise_email_to_requestor, - function(){ - return bluebird.resolve(); - } - ); -}; - - -Email.prototype.promise_leave_request_decision_emails = function(args){ - var self = this, - leave = args.leave, - action = args.action, - was_pended_revoke = args.was_pended_revoke, - send_mail = self.get_send_email(); - - var promise_email_to_supervisor = self.promise_rendered_email_template({ - template_name : 'leave_request_decision_to_supervisor', - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - action : action, - was_pended_revoke : was_pended_revoke, - user : leave.get('approver'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('approver').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('approver').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - var promise_email_to_requestor = self.promise_rendered_email_template({ - template_name : 'leave_request_decision_to_requestor', - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - action : action, - was_pended_revoke : was_pended_revoke, - user : leave.get('user'), - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('user').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('user').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - return bluebird.join( - promise_email_to_supervisor, promise_email_to_requestor, - function(){ - return bluebird.resolve(); - } - ); -}; - -Email.prototype.promise_forgot_password_email = function(args){ - var self = this, - user = args.user, - send_mail = self.get_send_email(); - - return user.getCompany() - .then(function(company){ - return self.promise_rendered_email_template({ - template_name : 'forgot_password', - context : { - user : user, - company : company, + const promise_email_to_supervisor = ({ + comments, + requesterAllowance + }) => supervisor => + self + .promise_rendered_email_template({ + template_name: template_name_to_supervisor, + context: { + leave, + comments, + approver: supervisor, + requester: leave.get('user'), + user: supervisor, + requesterAllowance } - }); - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : user.email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return user.record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); -}; - -Email.prototype.promise_reset_password_email = function(args){ - var self = this, - user = args.user, - send_mail = self.get_send_email(); - - return self.promise_rendered_email_template({ - template_name : 'reset_password', - context : { - user : user, - } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : user.email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return user.record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); -}; - -Email.prototype.promise_leave_request_cancel_emails = function(args){ - var self = this, - leave = args.leave, - send_mail = self.get_send_email(); - - var promise_email_to_supervisor = self.promise_rendered_email_template({ - template_name : 'leave_request_cancel_to_supervisor', - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('approver'), + }) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: supervisor.email, + subject: email_obj.subject, + html: email_obj.body + }).finally(() => supervisor.record_email_addressed_to_me(email_obj)) + ) + + const promise_email_to_requestor = ({ comments, requesterAllowance }) => + self + .promise_rendered_email_template({ + template_name: template_name_to_requestor, + context: { + leave, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver'), + requesterAllowance + } + }) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: leave.get('user').email, + subject: email_obj.subject, + html: email_obj.body + }).finally(() => + leave.get('user').record_email_addressed_to_me(email_obj) + ) + ) + + return Promise.all([ + getCommentsForLeave({ leave }), + leave.get('user').promise_allowance() + ]).then(([comments, requesterAllowance]) => + bluebird.join( + promise_email_to_requestor({ comments, requesterAllowance }), + leave + .get('user') + .promise_supervisors() + .map(supervisor => + promise_email_to_supervisor({ comments, requesterAllowance })( + supervisor + ) + ), + () => bluebird.resolve() + ) + ) +} + +Email.prototype.promise_leave_request_decision_emails = function(args) { + const self = this + const leave = args.leave + const action = args.action + const was_pended_revoke = args.was_pended_revoke + const send_mail = self.get_send_email() + + const promise_email_to_supervisor = async comments => { + try { + // Render the email template + const email_obj = await self.promise_rendered_email_template({ + template_name: 'leave_request_decision_to_supervisor', + context: { + leave, + action, + was_pended_revoke, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver') + } + }) + + console.log('email_obj', email_obj) // Debugging line + + // Send the email + await send_mail({ + from: config.get('smtp:from'), + to: leave.get('approver').email, + subject: email_obj.subject, + html: email_obj.body + }) + + // Record the email + await leave.get('approver').record_email_addressed_to_me(email_obj) + } catch (error) { + console.error('Error in promise_email_to_supervisor:', error) } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('approver').email, - subject : email_obj.subject, - html : email_obj.body, - }) - .then(function(send_result){ - return leave.get('approver').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - var promise_email_to_requestor = self.promise_rendered_email_template({ - template_name : 'leave_request_cancel_to_requestor', - context : { - leave : leave, - approver : leave.get('approver'), - requester : leave.get('user'), - user : leave.get('user'), + } + const promise_email_to_requestor = async comments => { + try { + // Render the email template + const email_obj = await self.promise_rendered_email_template({ + template_name: 'leave_request_decision_to_requestor', + context: { + leave, + action, + was_pended_revoke, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('user') + } + }) + + console.log('email_obj', email_obj) // Debugging line + + // Send the email + await send_mail({ + from: config.get('smtp:from'), + to: leave.get('user').email, + subject: email_obj.subject, + html: email_obj.body + }) + + // Record the email + await leave.get('user').record_email_addressed_to_me(email_obj) + } catch (error) { + console.error('Error in promise_email_to_requestor:', error) } - }) - .then(function(email_obj){ - - return send_mail({ - from : config.get('application_sender_email'), - to : leave.get('user').email, - subject : email_obj.subject, - html : email_obj.body, + } + + return getCommentsForLeave({ leave }).then(comments => + bluebird.join( + promise_email_to_supervisor(comments), + promise_email_to_requestor(comments), + () => bluebird.resolve() + ) + ) +} + +Email.prototype.promise_forgot_password_email = function(args) { + const self = this + const user = args.user + const send_mail = self.get_send_email() + + return user + .getCompany() + .then(company => + self.promise_rendered_email_template({ + template_name: 'forgot_password', + context: { + user, + company + } + }) + ) + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: user.email, + subject: email_obj.subject, + html: email_obj.body + }) + .then(send_results => bluebird.resolve(send_results)) + .finally(() => user.record_email_addressed_to_me(email_obj)) + ) +} + +Email.prototype.promise_reset_password_email = function(args) { + const self = this + const user = args.user + const send_mail = self.get_send_email() + + return self + .promise_rendered_email_template({ + template_name: 'reset_password', + context: { + user + } }) - .then(function(send_result){ - return leave.get('user').record_email_addressed_to_me(email_obj) - .then(function(){ return bluebird.resolve( send_result ); }); - }); - }); - - return bluebird.join( - promise_email_to_supervisor, promise_email_to_requestor, - function(){ - return bluebird.resolve(); - } - ); -}; + .then(email_obj => + send_mail({ + from: config.get('smtp:from'), + to: user.email, + subject: email_obj.subject, + html: email_obj.body + }) + .then(send_results => bluebird.resolve(send_results)) + .finally(() => user.record_email_addressed_to_me(email_obj)) + ) +} + +Email.prototype.promise_leave_request_cancel_emails = function(args) { + const self = this + const leave = args.leave + const send_mail = self.get_send_email() + + const promise_email_to_supervisor = comments => + self + .promise_rendered_email_template({ + template_name: 'leave_request_cancel_to_supervisor', + context: { + leave, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver') + } + }) + .then(emailObj => + send_mail({ + from: config.get('smtp:from'), + to: leave.get('approver').email, + subject: emailObj.subject, + html: emailObj.body + }).finally(() => + leave.get('approver').record_email_addressed_to_me(emailObj) + ) + ) + + const promise_email_to_requestor = comments => + self + .promise_rendered_email_template({ + template_name: 'leave_request_cancel_to_requestor', + context: { + leave, + comments, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('user') + } + }) + .then(emailObj => + send_mail({ + from: config.get('smtp:from'), + to: leave.get('user').email, + subject: emailObj.subject, + html: emailObj.body + }).finally(() => + leave.get('user').record_email_addressed_to_me(emailObj) + ) + ) + + return getCommentsForLeave({ leave }).then(comments => + bluebird.join( + promise_email_to_supervisor(comments), + promise_email_to_requestor(comments), + () => bluebird.resolve() + ) + ) +} -module.exports = Email; +module.exports = Email diff --git a/lib/error/index.js b/lib/error/index.js new file mode 100644 index 000000000..f989ecacc --- /dev/null +++ b/lib/error/index.js @@ -0,0 +1,81 @@ +'use strict' + +const Joi = require('joi') + +const schema_throw_user_error = [ + Joi.object().keys({ + system_error: Joi.string().required(), + user_error: Joi.string().required() + }), + Joi.string() +] + +function throw_user_error(args) { + const validate_args = Joi.validate(args, schema_throw_user_error) + + // Ensure all necesasry info was privided + if (validate_args.error) { + // Tricky case here as we failed to rais exception, so log all data we have + // and still rais generic exception + console.log( + 'throw_user_error got invalid parameters: ' + validate_args.annotate() + ) + console.dir(args) + throw new Error('Failed to throw user errors') + } + + let system_error_message, user_error_message + + // Special case when user is lazy and specified generic error message to be + // used for system and customer level + if (typeof args === 'string') { + system_error_message = user_error_message = args + } else { + system_error_message = args.system_error + user_error_message = args.user_error + } + + const exception = new Error(system_error_message) + + if (user_error_message) { + exception.user_error_message = user_error_message + } + + exception.tom_error = true + + throw exception +} + +function extract_system_error_message(error) { + if (!error) { + return null + } + + return error +} + +function extract_user_error_message(error) { + if (!error) { + return null + } + + if (typeof error === 'string') { + return error + } + + if (error.user_error_message) { + return error.user_error_message + } + + return 'N/A' +} + +module.exports = { + throw_user_error, + extract_user_error_message, + extract_system_error_message, + + throwUserError: throw_user_error, + extractUserErrorMessage: extract_user_error_message, + extractSystemErrorMessage: extract_system_error_message +} diff --git a/lib/middleware/ensure_user_is_admin.js b/lib/middleware/ensure_user_is_admin.js index 97d6d012d..d1c6c76b7 100644 --- a/lib/middleware/ensure_user_is_admin.js +++ b/lib/middleware/ensure_user_is_admin.js @@ -4,19 +4,18 @@ * In case of failure - redirects to the route. * * */ -"use strict"; +'use strict' -module.exports = function(req, res, next){ +module.exports = function(req, res, next) { + // User should be login to view settings pages + if (!req.user) { + return res.redirect_with_session(303, '/') + } - // User should be login to view settings pages - if ( !req.user ) { - return res.redirect_with_session(303, '/'); - } + // Only Admin users allowed to deal with settings pages + if (!req.user.is_admin()) { + return res.redirect_with_session(303, '/') + } - // Only Admin users allowed to deal with settings pages - if ( !req.user.is_admin() ) { - return res.redirect_with_session(303, '/'); - } - - next(); -}; + next() +} diff --git a/lib/middleware/flash_messages.js b/lib/middleware/flash_messages.js index e86c04939..ff961c021 100644 --- a/lib/middleware/flash_messages.js +++ b/lib/middleware/flash_messages.js @@ -19,51 +19,53 @@ * * */ -"use strict"; +'use strict' // Move flash object out of session and place it in to locals // so template is aware of it -module.exports = function(req, res, next){ - res.locals.flash = req.session.flash; - delete req.session.flash; +module.exports = function(req, res, next) { + res.locals.flash = req.session.flash + delete req.session.flash - // This is is a function that return custom function to be installed - // into session object as a method to add new message or array of - // messages. - var install_flash_array = function(key) { + // This is is a function that return custom function to be installed + // into session object as a method to add new message or array of + // messages. + function install_flash_array(key) { + return function(error_message) { + if (!this.flash) { + this.flash = {} + } + if (!Array.isArray(this.flash[key])) { + this.flash[key] = [] + } - return function(error_message){ - if ( ! this.flash ) { - this.flash = {}; - } - if ( ! Array.isArray( this.flash[key] ) ) { - this.flash[key] = []; - } + if (Array.isArray(error_message)) { + this.flash[key] = this.flash[key].concat(error_message) + } else { + this.flash[key].push(error_message) + } + } + } - if (Array.isArray(error_message)) { - this.flash[key] = this.flash[key].concat( error_message ); - } else { - this.flash[key].push( error_message ); - } - }; - }; + req.session.flash_error = install_flash_array('errors') + req.session.flash_message = install_flash_array('messages') - req.session.flash_error = install_flash_array('errors'); - req.session.flash_message = install_flash_array('messages'); + req.session.flash_has_errors = function() { + return ( + this.flash && + Array.isArray(this.flash.errors) && + this.flash.errors.length > 0 + ) + } - req.session.flash_has_errors = function(){ - return this.flash - && Array.isArray( this.flash.errors ) - && this.flash.errors.length > 0; - }; + req.session.keep_old = function() { + if (res.locals.flash) { + if (res.locals.flash.errors) this.flash_error(res.locals.flash.errors) + if (res.locals.flash.messages) { + this.flash_message(res.locals.flash.messages) + } + } + } - req.session.keep_old = function(){ - if (res.locals.flash) { - if (res.locals.flash.errors) this.flash_error( res.locals.flash.errors ); - if (res.locals.flash.messages) this.flash_message( res.locals.flash.messages ); - } - }; - - next(); - -}; + next() +} diff --git a/lib/middleware/session_aware_redirect.js b/lib/middleware/session_aware_redirect.js index bdd67ae96..b434769ba 100644 --- a/lib/middleware/session_aware_redirect.js +++ b/lib/middleware/session_aware_redirect.js @@ -1,5 +1,4 @@ - -"use strict"; +'use strict' /* Because a bug in express-session middleware, when working @@ -14,21 +13,19 @@ TODO: Consider to completly substitute redirect function with new logic. */ -module.exports = function(req, res, next){ - - res.redirect_with_session = function(a,b){ - - if (arguments.length === 2) { - req.session.save(function(err){ - res.redirect(a,b); - }); - } else { - req.session.save(function(err){ - res.redirect(a); - }); - } - return true; - }; - - next(); -}; +module.exports = function(req, res, next) { + res.redirect_with_session = function(a, b) { + if (arguments.length === 2) { + req.session.save(() => { + res.redirect(a, b) + }) + } else { + req.session.save(() => { + res.redirect(a) + }) + } + return true + } + + next() +} diff --git a/lib/middleware/withSession.js b/lib/middleware/withSession.js new file mode 100644 index 000000000..6390d1b01 --- /dev/null +++ b/lib/middleware/withSession.js @@ -0,0 +1,52 @@ +const session = require('express-session') +const SequelizeStore = require('connect-session-sequelize')(session.Store) + +const redis = require('redis') +const connectRedis = require('connect-redis') + +const config = require('../config') + +const createSessionMiddleware = ({ sequelizeDb }) => { + let store + + const storageType = config.get('sessions:store') + + if (storageType === 'redis') { + const RedisStore = connectRedis(session) + + const redisConfiguration = config.get('sessions:redis') + const { host, port } = redisConfiguration + if (!(host && port)) { + throw new Error('Missing configuration for Redis to use with Sessions') + } + const redisClient = redis.createClient({ host, port }) + + redisClient.on('error', err => { + throw new Error(`Failed to connect to Redis: ${err}`) + }) + redisClient.on('connect', err => { + if (err) { + throw new Error(`Failed to connect to Redis: ${err}`) + } + console.log('Connected to redis successfully') + }) + + store = new RedisStore({ client: redisClient }) + } else { + if (!sequelizeDb) { + throw new Error( + 'Database connection was not provided into Session store manager!' + ) + } + store = new SequelizeStore({ db: sequelizeDb }) + } + + return session({ + store, + secret: config.get('sessions:secret'), + resave: false, + saveUninitialized: false + }) +} + +module.exports = createSessionMiddleware diff --git a/lib/model/Report.js b/lib/model/Report.js new file mode 100644 index 000000000..84c915776 --- /dev/null +++ b/lib/model/Report.js @@ -0,0 +1,193 @@ +'use strict' + +const Promise = require('bluebird') +const moment = require('moment') + +const getUsersWithLeaves = ({ + company, + startDate = company.get_today(), + endDate = company.get_today(), + department_id = null +}) => { + let result = company.sequelize.models.Company.scope( + 'with_active_users' + ).findOne({ + where: { id: company.id } + }) + + result = result.then(company => Promise.resolve(company.users)) + + if (department_id) { + result = result.then(users => + Promise.resolve( + users.filter(u => String(u.department_id) === String(department_id)) + ) + ) + } + + result = result.then(users => + Promise.map(users, user => user.reload_with_leave_details({}), { + concurrency: 10 + }) + ) + + const filter = filterLeaves({ startDate, endDate }) + + result = result.then(users => + Promise.map( + users, + user => + user + .promise_my_active_leaves_ever() + .then(leaves => Promise.resolve(leaves.filter(filter))) + .then(leaves => Promise.resolve({ user, leaves })), + { concurrency: 10 } + ) + ) + + result = result.then(report => + report.map(({ user, leaves }) => ({ + user: { + id: user.id, + department: user.department.name, + email: user.email, + fullName: user.full_name() + }, + leaves: leaves.map(l => leaveIntoObject(l)) + })) + ) + + return result +} + +const filterLeaves = ({ startDate, endDate }) => leave => { + const sd = moment.utc(leave.get_start_leave_day().date) + const ed = moment.utc(leave.get_end_leave_day().date) + return ( + (sd.isSameOrAfter(startDate, 'day') && sd.isSameOrBefore(endDate, 'day')) || + (ed.isSameOrAfter(startDate, 'day') && ed.isSameOrBefore(endDate, 'day')) + ) +} + +const leaveIntoObject = leave => { + const dateFormat = 'YYYY-MM-DD' + const dateTimeStamp = 'YYYY-MM-DD HH:mm' + + const Leave = leave.sequelize.models.Leave + + const statusMap = { + [Leave.status_new()]: 'New', + [Leave.status_approved()]: 'Approved', + [Leave.status_rejected()]: 'Rejected', + [Leave.status_pended_revoke()]: 'Pended Revoke', + [Leave.status_canceled()]: 'Canceled' + } + + return { + startDate: moment.utc(leave.get_start_leave_day().date).format(dateFormat), + endDate: moment.utc(leave.get_end_leave_day().date).format(dateFormat), + dayPartStart: leave.day_part_start, + dayPartEnd: leave.day_part_end, + type: leave.leave_type ? leave.leave_type.name : 'N/A', + deductedDays: leave.leave_type ? leave.get_deducted_days_number() : 'N/A', + approver: leave.approver ? leave.approver.full_name() : 'N/A', + approverId: leave.approver_id, + status: statusMap[leave.status] || 'Unknown', + + id: leave.id, + employeeId: leave.user_id, + employeeFullName: leave.user ? leave.user.full_name() : 'N/A', + employeeLastName: leave.user ? leave.user.lastname : 'N/A', + departmentId: leave.user ? leave.user.departmentId : null, + departmentName: + leave.user && leave.user.department ? leave.user.department.name : 'N/A', + typeId: leave.leaveTypeId, + createdAt: moment.utc(leave.createdAt).format(dateFormat), + createdTimeStamp: moment + .utc(leave.createdAt) + .tz('America/Denver') + .format(dateTimeStamp), + + employee_comment: leave.employee_comment || '', + approver_comment: leave.approver_comment || '' + } +} + +const fetchLeavesForLeavesReport = async ({ + startDate, + endDate, + departmentId, + leaveTypeId, + actingUser, + dbModel +}) => { + let users = await dbModel.User.findAll({ + where: { company_id: actingUser.company_id } + }) + + // If department was provided in filter out everyone who is not part of it + if (departmentId) { + users = users.filter(u => `${u.DepartmentId}` === `${departmentId}`) + } + + // The way how we fetch the leaves is not most efficient way (mildly speaking) + // but we go in its favour because we reuse the existing code that does similar + // logic. Hopefully that would make the app more stable when we come to refactor + // underlying code. + + const leavesObjects = [] + + for (const user of users) { + await user.promise_schedule_I_obey() + let usersLeaves = await user.getMyActiveLeavesForDateRange({ + dateStart: startDate, + dateEnd: endDate + }) + + // Filter out redundant leave types if we are interesting in only particular one + if (leaveTypeId) { + usersLeaves = usersLeaves.filter( + l => `${l.leave_type_id}` === `${leaveTypeId}` + ) + } + + leavesObjects.push(...usersLeaves) + } + + // Get comments that were added to leaves, so we can enrich leave data later. + // The idea is to get all LEAVE comments into memory as a map and then use it + // to inject comment into each leave while cycling through them + const allComments = await dbModel.Comment.findAll({ + where: { + company_id: actingUser.company_id, + entity_type: dbModel.Comment.getEntityTypeLeave() + } + }) + + const commentsMap = allComments.reduce((m, c) => { + if (m[c.entity_id] === undefined) { + m[c.entity_id] = [c] + } else { + m[c.entity_id].push(c) + } + + return m + }, {}) + + let leaves = leavesObjects.map(l => leaveIntoObject(l)) + + leaves = leaves.map(l => ({ + ...l, + comment: commentsMap[l.id] + ? commentsMap[l.id].map(({ comment }) => comment).join('. ') + : '' + })) + + return { leaves } +} + +module.exports = { + getUsersWithLeaves, + fetchLeavesForLeavesReport, + leaveIntoObject +} diff --git a/lib/model/audit.js b/lib/model/audit.js new file mode 100644 index 000000000..527e6a7ae --- /dev/null +++ b/lib/model/audit.js @@ -0,0 +1,35 @@ +'use strict' + +const Bluebird = require('bluebird') +const Models = require('./db') + +const getAuditCaptureForUser = ({ byUser, forUser, newAttributes }) => () => { + const attributeUpdates = Object.keys(newAttributes) + .filter(k => String(newAttributes[k]) !== String(forUser[k])) + .map(attribute => + Models.Audit.create({ + company_id: byUser.company_id, + by_user_id: byUser.id, + entity_type: 'USER', + entity_id: forUser.id, + attribute, + old_value: String(forUser[attribute]), + new_value: String(newAttributes[attribute]) + }) + ) + + return Bluebird.map(attributeUpdates, f => f, { concurrency: 5 }) +} + +const getAudit = ({ company_id }) => + Models.Audit.findAll({ + where: { + company_id + }, + raw: true + }) + +module.exports = { + getAuditCaptureForUser, + getAudit +} diff --git a/lib/model/calculateCarryOverAllowance.js b/lib/model/calculateCarryOverAllowance.js new file mode 100644 index 000000000..69a5bdbc3 --- /dev/null +++ b/lib/model/calculateCarryOverAllowance.js @@ -0,0 +1,64 @@ +'use strict' + +const moment = require('moment') +const Promise = require('bluebird') + +const calculateCarryOverAllowance = ({ users }) => { + const yearFrom = moment + .utc() + .add(-1, 'y') + .year() + const yearTo = moment.utc().year() + + let flow = Promise.resolve(users) + + flow = flow.then(users => + Promise.map( + users, + user => { + let carryOver + return Promise.resolve( + user.getCompany().then(c => (carryOver = c.carry_over)) + ) + .then(() => + user.reload_with_leave_details({ + year: moment.utc(yearFrom, 'YYYY') + }) + ) + .then(user => + user.promise_allowance({ + year: moment.utc(yearFrom, 'YYYY'), + now: moment.utc(yearFrom, 'YYYY').endOf('year'), + forceNow: true + }) + ) + .then(allowance => { + const carried_over_allowance = + carryOver === 0 + ? 0 + : Math.min( + allowance.number_of_days_available_in_allowance, + carryOver + ) + + return user.promise_to_update_carried_over_allowance({ + carried_over_allowance, + year: yearTo + }) + }) + .then(() => + console.log( + `Carried over unused allowance ${yearFrom} -> ${yearTo} for user ${ + user.id + }` + ) + ) + }, + { concurrency: 1 } + ) + ) + + return flow +} + +module.exports = { calculateCarryOverAllowance } diff --git a/lib/model/calendar_month.js b/lib/model/calendar_month.js index 85b9a9fa1..028c5ac15 100644 --- a/lib/model/calendar_month.js +++ b/lib/model/calendar_month.js @@ -1,229 +1,415 @@ +'use strict' -'use strict'; +const moment = require('moment') +const _ = require('underscore') -var moment = require('moment'), - _ = require('underscore'); +function CalendarMonth(day, args) { + const self = this + this.date = moment.utc(day).startOf('month') + this._leaves = {} + this._bank_holidays = {} -function CalendarMonth(day, args){ - var me = this; - this.date = moment(day).startOf('month'); - this._leaves = {}; - this._bank_holidays = {}; + if (args && args.today) { + self.today = args.today + } else { + throw new Error( + 'CalendarMonth requires today - moment object that represents today' + ) + } - if (args){ - this._schedule = args.schedule; - } + if (args) { + self._schedule = args.schedule + } - if ( ! this._schedule ) { - throw new Error('CalendarMonth requires schedule'); - } + if (!self._schedule) { + throw new Error('CalendarMonth requires schedule') + } - if (args && args.bank_holidays){ - var map = {}; - args.bank_holidays.forEach(function(day){ - day = moment(day); - map[day.format(me.default_date_format())] = day; - }); - this._bank_holidays = map; - } + // fixed `today` always appearing as holiday + // due to invalid moment and arg.bank_holidays combo + if (args && args.bank_holidays) { + const map = {} + const bArray = args.bank_holidays + // console.log('bArray: ', bArray) + + bArray.forEach(d => { + let parsedDate + + if (typeof d === 'string' || d instanceof String) { + parsedDate = moment.utc(d) + d = { + date: parsedDate, + name: undefined + } + } else if (d instanceof Date) { + parsedDate = moment.utc(d) + d = { + date: parsedDate, + name: undefined + } + } else if (d.date && moment.utc(d.date).isValid()) { + parsedDate = moment.utc(d.date) + d = { + date: parsedDate, + name: d.name + } + } else { + // Skip invalid entries + return + } - if (args && args.leave_days){ - var map = {}; - args.leave_days.forEach(function(day){ - var attribute = moment(day.date).format(me.default_date_format()); - if ( ! map[attribute] ) { - map[attribute] = day; - } else if ( map[attribute] ) { - - if (map[attribute].is_all_day_leave()) { - return; - } - - if (day.is_all_day_leave()){ - map[attribute] = day; - } else if ( map[attribute].day_part !== day.day_part ) { - map[attribute].pretend_to_be_full_day(); - } - } - }); - this._leaves = map; - } -}; + const formattedDate = d.date.clone().format(self.default_date_format()) + map[formattedDate] = d + }) + + self._bank_holidays = map + } + + if (args && args.leave_days) { + var map = {} + args.leave_days.forEach(day => { + const attribute = moment.utc(day.date).format(self.default_date_format()) + if (!map[attribute]) { + map[attribute] = day + } else if (map[attribute]) { + if (map[attribute].is_all_day_leave()) { + return + } + + if (day.is_all_day_leave()) { + map[attribute] = day + } else if (map[attribute].day_part !== day.day_part) { + // Merge leave types from both days into one in "map" + if (day.is_morning_leave()) { + map[attribute].morning_leave_type_id = day.morning_leave_type_id + } + + if (day.is_afternoon_leave()) { + map[attribute].afternoon_leave_type_id = day.afternoon_leave_type_id + } + + map[attribute].pretend_to_be_full_day() + } + } + }) + self._leaves = map + } + + self._leave_types_map = {} + + if (args && args.leave_types) { + // Build leave types look up dictionary + args.leave_types.forEach(lt => (self._leave_types_map[lt.id] = lt)) + } +} CalendarMonth.prototype.is_bank_holiday = function(day) { - return this._bank_holidays && this._bank_holidays[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; -}; + const formattedDate = this.get_base_date() + .date(day) + .format(this.default_date_format()) + + let list = + this._bank_holidays && this._bank_holidays.hasOwnProperty(formattedDate) + + return list +} + +CalendarMonth.prototype.get_bank_holiday_name = function(day) { + const self = this + + if (!self.is_bank_holiday(day)) { + return null + } + + // console.log('this._bank_holidays: ', this._bank_holidays) + return this._bank_holidays[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ].name +} CalendarMonth.prototype.is_leave = function(day) { - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] - return leave_day && leave_day.is_all_day_leave(); -}; + return leave_day && leave_day.is_all_day_leave() +} CalendarMonth.prototype.is_leave_morning = function(day) { - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; - return this.is_leave(day) - || (leave_day && leave_day.is_morning_leave()); -}; + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] + return this.is_leave(day) || (leave_day && leave_day.is_morning_leave()) +} CalendarMonth.prototype.is_leave_afternoon = function(day) { - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; - - return this.is_leave(day) - || (leave_day && leave_day.is_afternoon_leave()); -}; - -CalendarMonth.prototype.get_leave_obj = function(day){ - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] + + return this.is_leave(day) || (leave_day && leave_day.is_afternoon_leave()) +} + +CalendarMonth.prototype.get_leave_obj = function(day) { + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] if (leave_day && leave_day.leave) { - return leave_day.leave; + return leave_day.leave } else { - return null; + return null + } +} + +CalendarMonth.prototype.is_new_leave = function(day) { + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] + + return leave_day && leave_day.leave && leave_day.leave.is_new_leave() +} + +CalendarMonth.prototype.is_approved_leave = function(day) { + const leave_day = this._leaves[ + this.get_base_date() + .date(day) + .format(this.default_date_format()) + ] + + return leave_day && leave_day.leave && leave_day.leave.is_approved_leave() +} + +CalendarMonth.prototype.get_morning_leave_type_id = function(day) { + const self = this + const leave_day = + self._leaves[ + self + .get_base_date() + .date(day) + .format(self.default_date_format()) + ] + + if (!leave_day) { + return null } -}; - -CalendarMonth.prototype.is_new_leave = function(day){ - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; - - return leave_day && leave_day.leave && leave_day.leave.is_new_leave(); -}; -CalendarMonth.prototype.is_approved_leave = function(day){ - var leave_day = this._leaves[ - this.get_base_date().date(day).format(this.default_date_format()) - ]; + return leave_day.get_morning_leave_type_id() +} + +CalendarMonth.prototype.get_afternoon_leave_type_id = function(day) { + const self = this + const leave_day = + self._leaves[ + self + .get_base_date() + .date(day) + .format(self.default_date_format()) + ] + + if (!leave_day) { + return null + } - return leave_day && leave_day.leave && leave_day.leave.is_approved_leave(); -}; + return leave_day.get_afternoon_leave_type_id() +} -CalendarMonth.prototype.default_date_format = function(){ - return 'YYYY-MM-DD'; -}; +CalendarMonth.prototype.default_date_format = function() { + return 'YYYY-MM-DD' +} CalendarMonth.prototype.how_many_days = function() { - return this.date.daysInMonth(); -}; - -CalendarMonth.prototype.get_base_date = function(){ - return this.date.clone(); -}; - -CalendarMonth.prototype._week_day_map = function(){ - return { 0:7, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6 }; -}; - -CalendarMonth.prototype.week_day = function(){ - return this._week_day_map()[ this.date.day() ]; -}; - -CalendarMonth.prototype.how_many_blanks_at_the_start = function(){ - return this.week_day() - 1; -}; - -CalendarMonth.prototype.how_many_blanks_at_the_end = function(){ - return 7 - this._week_day_map()[ this.get_base_date().endOf('month').day() ]; -}; - -CalendarMonth.prototype.is_weekend = function(day){ - return ! this._schedule.is_it_working_day({ - day : this.get_base_date().date(day), - }); -}; - -CalendarMonth.prototype.is_current_day = function(day){ - return this.get_base_date().date(day).format( this.default_date_format() ) - === - moment().format( this.default_date_format() ); -}; - -CalendarMonth.prototype.as_for_template = function(){ - var weeks = []; - var week = []; - - for( var i=0; i= 7) { - weeks.push( week ); - week = []; - } + if (self.is_current_day(i)) { + day.is_current_day = true } - for( var i=0; i= 7) { + weeks.push(week) + week = [] } + } - weeks.push(week); + for (let i = 0; i < self.how_many_blanks_at_the_end(); i++) { + week.push({ val: '' }) + } + + weeks.push(week) - return { - month : this.get_base_date().format('MMMM'), - moment: this.get_base_date(), - weeks : weeks, - }; -}; + return { + month: self.get_base_date().format('MMMM'), + moment: self.get_base_date(), + weeks + } +} -CalendarMonth.prototype.as_for_team_view = function(){ - var me = this, - for_calendar_structure = this.as_for_template(); +CalendarMonth.prototype.as_for_team_view = function() { + const me = this + const for_calendar_structure = this.as_for_template() // remove empty days, those staying for empty cells in calendar - var days = _.filter( + const days = _.filter( _.flatten(for_calendar_structure.weeks), - function(day){ - return !! day.val - } - ); - - _.each(days, function(day){ - day.moment = me.get_base_date().date(day.val); - }); + day => !!day.val + ) - return days; + _.each(days, day => { + day.moment = me.get_base_date().date(day.val) + }) -}; + return days +} -module.exports = CalendarMonth; +module.exports = CalendarMonth diff --git a/lib/model/comment.js b/lib/model/comment.js new file mode 100644 index 000000000..706cfcf76 --- /dev/null +++ b/lib/model/comment.js @@ -0,0 +1,27 @@ +'use strict' + +const Bluebird = require('bluebird') +const Models = require('./db') + +const commentLeave = ({ leave, comment, company_id }) => + Models.Comment.create({ + entity_type: Models.Comment.getEntityTypeLeave(), + entity_id: leave.id, + comment, + company_id, + by_user_id: leave.user_id + }) + +const getCommentsForLeave = ({ leave }) => + Models.Comment.findAll({ + raw: true, + where: { + entity_type: Models.Comment.getEntityTypeLeave(), + entity_id: leave.id + } + }) + +module.exports = { + commentLeave, + getCommentsForLeave +} diff --git a/lib/model/company/exporter/index.js b/lib/model/company/exporter/index.js new file mode 100644 index 000000000..6aedd082e --- /dev/null +++ b/lib/model/company/exporter/index.js @@ -0,0 +1,64 @@ +'use strict' + +const Joi = require('joi') +const Promise = require('bluebird') +const CompanySummary = require('./summary') + +const constructor_schema = Joi.object() + .required() + .keys({ + dbSchema: Joi.object().required() + }) +const schemaPromiseCompanySummary = Joi.object() + .required() + .keys({ + company: Joi.object().required() + }) + +class CompanyExporter { + constructor(args) { + args = Joi.attempt( + args, + constructor_schema, + 'Faled to instantiate new companyExporter due to arguments validation' + ) + + this._db_model = args.dbSchema + } + + get dbModel() { + return this._db_model + } + + promiseCompanySummary(args) { + args = Joi.attempt( + args, + schemaPromiseCompanySummary, + 'Failed to get company summary die to validation errors' + ) + + const self = this + const company = args.company + + return Promise.join( + self.dbModel.Company.scope( + 'with_simple_departments', + 'with_leave_types' + ).findOne({ + where: { id: company.id } + }), + self.dbModel.User.scope('with_simple_leaves').findAll({ + where: { company_id: company.id } + }), + (company, users) => + Promise.resolve( + new CompanySummary({ + company, + users + }) + ) + ) + } +} + +module.exports = CompanyExporter diff --git a/lib/model/company/exporter/summary.js b/lib/model/company/exporter/summary.js new file mode 100644 index 000000000..55cedece0 --- /dev/null +++ b/lib/model/company/exporter/summary.js @@ -0,0 +1,119 @@ +'use strict' + +const Promise = require('bluebird') +const CSV = Promise.promisifyAll(require('csv')) +const moment = require('moment') +const Joi = require('joi') + +const schema_constructor = Joi.object() + .required() + .keys({ + company: Joi.object().required(), + users: Joi.array().required() + }) + +const { sorter } = require('../../../util') + +class CompanySummary { + constructor(args) { + args = Joi.attempt( + args, + schema_constructor, + 'Failed to in stantiate companySummary due to validation' + ) + + this._company = args.company + this._users = args.users + } + + get company() { + return this._company + } + + get users() { + return this._users + } + + as_csv_data() { + const self = this + const results = [] + const date_format = self.company.get_default_date_format() + const departmentsMap = {} + const leaveTypesMap = {} + + // Fill departments map + self.company.departments.forEach(d => (departmentsMap[d.id] = d)) + + // Fill leave types map + self.company.leave_types.forEach(lt => (leaveTypesMap[lt.id] = lt)) + + // Put headers + results.push([ + 'Department', + 'Last name', + 'Name', + 'Email address', + 'Type of absence', + 'Started at', + 'Date type', + 'Ended at', + 'Date type' + ]) + + self.users + // Sort users by departments and by last names + .sort( + (a, b) => + sorter( + departmentsMap[a.department_id].name, + departmentsMap[b.department_id].name + ) || sorter(a.lastname, b.lastname) + ) + // Get a row per every leave + .forEach(u => + u.my_leaves.forEach(l => { + const start_date = moment(l.date_start).format(date_format) + const end_date = moment(l.date_end).format(date_format) + + const startDatePart = l.does_start_half_morning() + ? 'Morning' + : l.does_start_half_afternoon() + ? 'Afternoon' + : 'All Day' + + const endDatePart = + start_date === end_date + ? startDatePart + : l.does_end_half_morning() + ? 'Morning' + : l.does_end_half_afternoon() + ? 'Afternoon' + : 'All Day' + + results.push([ + departmentsMap[u.department_id].name, + u.lastname, + u.name, + u.email, + leaveTypesMap[l.leave_type_id].name, + start_date, + startDatePart, + end_date, + endDatePart + ]) + }) + ) + + return results + } + + promise_as_csv_string() { + const self = this + + return Promise.resolve(self.as_csv_data()).then(data => + CSV.stringifyAsync(data) + ) + } +} + +module.exports = CompanySummary diff --git a/lib/model/company/remover.js b/lib/model/company/remover.js new file mode 100644 index 000000000..7a134e77a --- /dev/null +++ b/lib/model/company/remover.js @@ -0,0 +1,88 @@ +'use strict' + +const Joi = require('joi') +const Promise = require('bluebird') +const Exception = require('../../error') +const Models = require('../db') + +const schemaPromiseToRemove = Joi.object() + .required() + .keys({ + company: Joi.object().required(), // .type(Models.Company.constructor), + byUser: Joi.object().required(), // .type(Models.User.constructor), + confirmName: Joi.string() + .required() + .trim() + }) + +class CompanyRemover { + static promiseToRemove(args) { + args = Joi.attempt( + args, + schemaPromiseToRemove, + 'Param validation failed for promiseToRemove' + ) + + const company = args.company + const byUser = args.byUser + const confirmName = args.confirmName + + // Ensure that confirm name is correct + const normalizedNames = [company.name, confirmName] + .map(s => s.trim()) + .map(s => s.replace(/\s+/g, '')) + .map(s => s.toUpperCase()) + + if (normalizedNames[0] !== normalizedNames[1]) { + Exception.throw_user_error({ + system_error: `Confirmed name does not match one on company record: ${normalizedNames.join( + ', ' + )}`, + user_error: 'Provided name confirmation does not match company one' + }) + } + + return ( + Models.User + + // Ensure user belongs to current company and is admin + .count({ + where: { + id: byUser.id, + company_id: company.id, + admin: true + } + }) + .then(count => { + if (count === 0) { + Exception.throw_user_error({ + system_error: `An attempt to remove company [${ + company.id + }] by unrelated user [${byUser.id}]`, + user_error: 'User does not have permissions to remove company' + }) + } + return Promise.resolve(1) + }) + + // Remove company record and all related records + // (we do not really remove all data, just the sensitive information) + // .. delete email audit + .then(() => + Models.EmailAudit.destroy({ where: { company_id: company.id } }) + ) + // Remove all leaves for related users + .then(() => + company + .getUsers() + .map(u => Models.Leave.destroy({ where: { user_id: u.id } })) + ) + // Remove all users + .then(() => Models.User.destroy({ where: { company_id: company.id } })) + // Remove company record + .then(() => company.destroy()) + ) + } +} + +module.exports = CompanyRemover diff --git a/lib/model/db/audit.js b/lib/model/db/audit.js new file mode 100644 index 000000000..8ff59563b --- /dev/null +++ b/lib/model/db/audit.js @@ -0,0 +1,57 @@ +'use strict' + +module.exports = function(sequelize, DataTypes) { + const Audit = sequelize.define( + 'Audit', + { + entity_type: { + type: DataTypes.STRING, + allowNull: false, + comment: + 'Type of the entity which change is tracked. E.g. USER, LEAVE etc' + }, + entity_id: { + type: DataTypes.INTEGER, + allowNull: false, + comment: 'ID of the entity defined by entityType' + }, + attribute: { + type: DataTypes.STRING, + allowNull: false, + comment: 'Attribute of the entity which change is to be recorded' + }, + old_value: { + type: DataTypes.STRING, + allowNull: true, + comment: 'Old value converted to STRING' + }, + new_value: { + type: DataTypes.STRING, + allowNull: true, + comment: 'New value converted to STRING' + } + }, + { + underscored: true, + freezeTableName: true, + timestamps: true, + createdAt: 'at', + updatedAt: false, + tableName: 'audit' + } + ) + + Audit.associate = models => { + Audit.belongsTo(models.Company, { + as: 'company', + foreignKey: 'company_id' + }) + + Audit.belongsTo(models.User, { + as: 'byUser', + foreignKey: { name: 'by_user_id', allowNull: true } + }) + } + + return Audit +} diff --git a/lib/model/db/bank_holiday.js b/lib/model/db/bank_holiday.js index b4249bc30..ffa471184 100644 --- a/lib/model/db/bank_holiday.js +++ b/lib/model/db/bank_holiday.js @@ -1,72 +1,75 @@ -"use strict"; +'use strict' -var moment = require('moment'), -_ = require('underscore'), -config = require('../../config'); +const moment = require('moment') +const _ = require('underscore') +const config = require('../../config') module.exports = function(sequelize, DataTypes) { - var BankHoliday = sequelize.define("BankHoliday", { - // TODO add validators! - name : { - type : DataTypes.STRING, - allowNull : false - }, - date : { - type : DataTypes.DATE, - allowNull : false, - }, - }, { - indexes : [ - { - fields : ['companyId'], - } - ], - classMethods: { - associate : function( models ) { - BankHoliday.belongsTo(models.Company, {as : 'company'}); - }, + const BankHoliday = sequelize.define( + 'BankHoliday', + { + // TODO add validators! + name: { + type: DataTypes.STRING, + allowNull: false + }, + date: { + type: DataTypes.DATE, + allowNull: false + } + }, + { + underscored: true, + indexes: [ + { + fields: ['company_id'] + } + ] + } + ) - generate_bank_holidays : function(args){ - var company = args.company, - country_code = args.country_code; + BankHoliday.associate = function(models) { + BankHoliday.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: false } + }) + } - var bank_holidays = [ - { - name : 'Early May bank holiday', - date : '2015-05-04', - companyId : company.id, - }, - ]; + BankHoliday.generate_bank_holidays = function(args) { + const company = args.company + const country_code = args.country_code - var config_countries = config.get('countries'); + let bank_holidays = [ + { + name: 'Early May bank holiday', + date: '2015-05-04', + company_id: company.id + } + ] - if ( - config_countries.hasOwnProperty( country_code ) - && config_countries[ country_code ].hasOwnProperty('bank_holidays') - && config_countries[ country_code ].bank_holidays.length > 0 - ) { - bank_holidays = _.map( - config_countries[ country_code ].bank_holidays, - function(bh){ - return { - name : bh.name, - date : bh.date, - companyId : company.id, - }; - } - ); - } + const config_countries = config.get('countries') - return BankHoliday.bulkCreate(bank_holidays); - }, - }, + if ( + config_countries.country_code && + config_countries[country_code].bank_holidays && + config_countries[country_code].bank_holidays.length > 0 + ) { + bank_holidays = _.map( + config_countries[country_code].bank_holidays, + bh => ({ + name: bh.name, + date: bh.date, + company_id: company.id + }) + ) + } - instanceMethods : { - get_pretty_date : function(){ - return moment(this.date).format('YYYY-MM-DD'); - }, - } - }); + return BankHoliday.bulkCreate(bank_holidays) + } + + BankHoliday.prototype.get_pretty_date = function() { + return moment.utc(this.date).format('YYYY-MM-DD') + } - return BankHoliday; -}; + return BankHoliday +} diff --git a/lib/model/db/comment.js b/lib/model/db/comment.js new file mode 100644 index 000000000..ce360a7ab --- /dev/null +++ b/lib/model/db/comment.js @@ -0,0 +1,48 @@ +'use strict' + +module.exports = (sequelize, DataTypes) => { + const Comment = sequelize.define( + 'Comment', + { + entity_type: { + type: DataTypes.STRING, + allowNull: false, + comment: 'Type of entity current comments belongs to' + }, + entity_id: { + type: DataTypes.INTEGER, + allowNull: false, + comment: 'Reference ID for entity current comment belongs to' + }, + comment: { + type: DataTypes.TEXT, + allowNull: false, + comment: 'The value of comment' + } + }, + { + underscored: true, + freezeTableName: true, + timestamps: true, + createdAt: 'at', + updatedAt: false, + tableName: 'comments' + } + ) + + Comment.associate = models => { + Comment.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: false } + }) + + Comment.belongsTo(models.User, { + as: 'byUser', + foreignKey: { name: 'by_user_id', allowNull: false } + }) + } + + Comment.getEntityTypeLeave = () => 'LEAVE' + + return Comment +} diff --git a/lib/model/db/company.js b/lib/model/db/company.js index e638450a3..83f3b445f 100644 --- a/lib/model/db/company.js +++ b/lib/model/db/company.js @@ -1,519 +1,521 @@ -"use strict"; - -var Promise = require("bluebird"), - LdapAuth = require('ldapauth-fork'), - moment = require('moment'), - _ = require('underscore'); - -module.exports = function(sequelize, DataTypes) { - var Company = sequelize.define("Company", { - // TODO add validators! - name : { - type : DataTypes.STRING, - allowNull : false - }, - country : { - type : DataTypes.STRING, - allowNull : false - }, - start_of_new_year : { - type : DataTypes.INTEGER, - allowNull : false - }, - share_all_absences : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : false, - }, - ldap_auth_enabled : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : false, - }, - ldap_auth_config : { - type : DataTypes.STRING, - allowNull : true, - set : function(val){ - if (! val || typeof val !== 'object' ) { - val = JSON.stringify({}); +'use strict' + +const Promise = require('bluebird') +const LdapAuth = require('ldapauth-fork') +const moment = require('moment') +const moment_tz = require('moment-timezone') +const _ = require('underscore') +const uuidv4 = require('uuid/v4') +const { sorter } = require('../../util') + +module.exports = function (sequelize, DataTypes) { + const Company = sequelize.define( + 'Company', + { + // TODO add validators! + name: { + type: DataTypes.STRING, + allowNull: false + }, + country: { + type: DataTypes.STRING, + allowNull: false + }, + start_of_new_year: { + type: DataTypes.INTEGER, + allowNull: false + }, + share_all_absences: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + is_team_view_hidden: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'Determise if Team View is hidden for non-admin users' + }, + ldap_auth_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + ldap_auth_config: { + type: DataTypes.TEXT, + allowNull: true, + set: function (val) { + if (!val || typeof val !== 'object') { + val = JSON.stringify({}) } - this.setDataValue( 'ldap_auth_config', JSON.stringify(val) ); + this.setDataValue('ldap_auth_config', JSON.stringify(val)) }, - get : function(){ - var val = this.getDataValue('ldap_auth_config'); + get: function () { + let val = this.getDataValue('ldap_auth_config') try { - val = JSON.parse(val); - } catch(err) { + val = JSON.parse(val) + } catch (err) { console.error( 'Faled to parse the LDAP settings saved in company %s. Error: %s', - this.id, err - ); - val = {}; + this.id, + err + ) + val = {} } - return val; - }, - }, - date_format : { - type : DataTypes.STRING, - allowNull : false, - defaultValue : 'YYYY-MM-DD', - }, - }, { - - indexes : [ - { - fields : ['id'], - } - ], - - classMethods: { - - associate : function( models ) { - Company.hasMany(models.Department, { - as : 'departments', - foreignKey : 'companyId', - }); - Company.hasMany(models.User, { - as : 'users', - foreignKey : 'companyId', - }); - Company.hasMany(models.BankHoliday, { - as : 'bank_holidays', - foreignKey : 'companyId', - }); - Company.hasMany(models.LeaveType, { - as : 'leave_types', - foreignKey : 'companyId', - }); - Company.hasMany(models.EmailAudit, { - as : 'audit_emails', - foreignKey : 'company_id', - }); + return val + } }, - - // Create new company based on default values - create_default_company : function(args){ - var country_code = args.country_code || 'UK'; - - // Add new company record - return Company.create({ - name : args.name || 'New company', - country : country_code, - start_of_new_year : 1, - }) + date_format: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'YYYY-MM-DD' + }, + company_wide_message: { + type: DataTypes.TEXT, + allowNull: true, + defaultValue: null, + comment: 'Message shown to all users that belong to current company' + }, + mode: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1, + comment: 'Indicate which mode the company account is in.' + }, + timezone: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: 'Europe/London', + comment: 'Timezone current company is located in' + }, + integration_api_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + integration_api_token: { + type: DataTypes.UUID, + allowNull: true, + defaultValue: () => uuidv4(), + comment: 'Indicate which mode the company account is in.' + }, + carry_over: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: 0, + comment: + 'Defines how may remaining days from allowance are carried over to the next year.' + } + }, + { + underscored: true, + indexes: [ + { + fields: ['id'] + } + ] + } + ) + + Company.associate = function (models) { + Company.hasMany(models.Department, { + as: 'departments', + foreignKey: { name: 'company_id', allowNull: false } + }) + Company.hasMany(models.User, { + as: 'users', + foreignKey: { name: 'company_id', allowNull: false } + }) + Company.hasMany(models.BankHoliday, { + as: 'bank_holidays', + foreignKey: { name: 'company_id', allowNull: false } + }) + Company.hasMany(models.LeaveType, { + as: 'leave_types', + foreignKey: { name: 'company_id', allowNull: false } + }) + Company.hasMany(models.EmailAudit, { + as: 'audit_emails', + foreignKey: { name: 'company_id', allowNull: false } + }) + Company.hasMany(models.Audit, { + as: 'audit', + foreignKey: { name: 'company_id', allowNull: false } + }) + } + + Company.loadScope = function (models) { + Company.addScope('with_all_users', { + include: [{ model: models.User, as: 'users' }] + }) + + Company.addScope('with_audit', { + include: [{ model: models.Audit, as: 'audit' }] + }) + + Company.addScope( + 'with_active_users', + // The scope needs to be dynamic as the criteria for active users is based + // on current date, which could be buggy if left static + () => ({ + include: [ + { + model: models.User, + as: 'users', + where: models.User.get_active_user_filter() + } + ] + }) + ) + + Company.addScope('order_by_active_users', { + order: [[{ model: models.User, as: 'users' }, 'lastname']] + }) + + Company.addScope('with_simple_departments', { + include: [{ model: models.Department, as: 'departments' }] + }) + + Company.addScope('with_bank_holidays', { + include: [{ model: models.BankHoliday, as: 'bank_holidays' }] + }) + + Company.addScope('order_by_bank_holidays', { + order: [[{ model: models.BankHoliday, as: 'bank_holidays' }, 'date']] + }) + + Company.addScope('with_leave_types', { + include: [{ model: models.LeaveType, as: 'leave_types' }] + }) + } + + Company.scopeAssociate = function (models) { + // Following code is here for reference only: to help declaring scopped + // associations in future. The approach is not suitable for active users + // bacause of its dynamic nature - we need to have up to date "now", which + // does not work nicely with accociations as they are installed once at the + // applicaiton start time, and if the app will be running for more than one + // day the active users association becomes wrong as it still remember the + // now as a date when the app was started + // + // Company.hasMany(models.User.scope('active'), { + // as : 'activeUsers', + // foreignKey : 'company_id', + // }); + } + + // Return code for "read-only holidays" mode of company account. + // That means company only shows holidays/timeoff for emplyes and + // does not allow to create new ones. + // + Company.get_mode_readonly_holidays = function () { + return 2 + } + + // Create new company based on default values + Company.create_default_company = function (args) { + const country_code = args.country_code || 'UK' + const timezone = args.timezone || 'Europe/London' + + // Add new company record + return ( + Company.create({ + name: args.name || 'New company', + country: country_code, + start_of_new_year: 1, + timezone + }) // When new company is created - add default departments to it - .then(function(company){ - - return Promise.all([ - sequelize.models.Department - .create({ - name : 'Sales', - companyId : company.id, - }), - sequelize.models.BankHoliday - .generate_bank_holidays({ - company : company, - country_code : country_code, - }), - sequelize.models.LeaveType - .generate_leave_types({ company : company }) - ]) - .then(function(){ - - return Promise.resolve(company); - }); - }); - - }, + .then(company => + Promise.all([ + sequelize.models.Department.create({ + name: 'Sales', + company_id: company.id + }), + sequelize.models.BankHoliday.generate_bank_holidays({ + company, + country_code + }), + sequelize.models.LeaveType.generate_leave_types({ + company + }) + ]).then(() => Promise.resolve(company)) + ) + ) + } + + Company.getCompanyByApiToken = ({ token }) => + sequelize.models.Company.scope('with_active_users').findOne({ + where: { + integration_api_token: token, + integration_api_enabled: true + } + }) + + Company.restore_from_dump = args => { + // The dump JSON object that is about to be imported + const dump_json = args.dump_json + // Dictionary that holds mapping between promary keys from original system + // and new one + const id_maps = { + company: {}, + bank_holiday: {}, + leave_type: {}, + department: {}, + user: {} + } - restore_from_dump : function(args){ - - // The dump JSON object that is about to be imported - var dump_json = args.dump_json, - - // Dictionary that holds mapping between promary keys from original system - // and new one - id_maps = { - company : {}, - bank_holiday : {}, - leave_type : {}, - department : {}, - user : {}, - }; - - // Make sure that emails that are about to be migrated do not exist - // withing current database - return sequelize.models.User.findAll({ - where : { - email : { "$in" : _.map(dump_json.users, function(u){ return u.email; }) } + // Make sure that emails that are about to be migrated do not exist + // withing current database + return ( + sequelize.models.User.findAll({ + where: { + email: { + $in: _.map(dump_json.users, u => u.email) } - }) - .then(function(users){ + } + }) + .then(users => { if (users.length > 0) { throw new Error( - 'Users with following emails already exist in the system: ' - + _.map(users, function(u){return u.email}).join(', ') - ); + 'Users with following emails already exist in the system: ' + + _.map(users, u => u.email).join(', ') + ) } return sequelize.models.Company.describe() }) // Instert company - .then(function(company_definition){ - - var company_json = _.omit(dump_json, function(value, key, object){ - return ! (company_definition.hasOwnProperty(key) && key !== 'id'); - }); + .then(company_definition => { + const company_json = _.omit( + dump_json, + (value, key, object) => + !(company_definition.hasOwnProperty(key) && key !== 'id') + ) - return sequelize.models.Company.create(company_json) - .then(function(company){ - id_maps.company[dump_json.id] = company.id; - return Promise.resolve(1); - }); + return sequelize.models.Company.create(company_json).then(company => { + id_maps.company[dump_json.id] = company.id + return Promise.resolve(1) + }) }) // Instert bank holidays and Leave types // - .then(function(){ - return Promise.join( + .then(() => + Promise.join( promise_to_restore_bank_holidays({ - dump_json : dump_json, - id_maps : id_maps, - model : sequelize.models, + dump_json, + id_maps, + model: sequelize.models }), promise_to_restore_leave_types({ - dump_json : dump_json, - id_maps : id_maps, - model : sequelize.models, + dump_json, + id_maps, + model: sequelize.models }), - function(){return Promise.resolve(1);} - ); - }) + () => Promise.resolve(1) + ) + ) // Insert departments // - .then(function(){ - return promise_to_restore_departments({ - dump_json : dump_json, - id_maps : id_maps, - model : sequelize.models, - }); - }) + .then(() => + promise_to_restore_departments({ + dump_json, + id_maps, + model: sequelize.models + }) + ) // insert users // - .then(function(){ - return promise_to_restore_users({ - dump_json : dump_json, - id_maps : id_maps, - model : sequelize.models, - }); - }) + .then(() => + promise_to_restore_users({ + dump_json, + id_maps, + model: sequelize.models + }) + ) - // Update departments with correct user IDs for superviser + // Update departments with correct user IDs for supervisor // - .then(function(){ - return Promise.all( - _.map(_.values( id_maps.department ), function(department_id){ - return sequelize.models.Department - .find({ where : {id : department_id} }); - }) - ) - .then(function(departments_to_update){ - return Promise.all( - _.map(departments_to_update, function(department){ - department.bossId = id_maps.user[ department.bossId ]; - return department.save(); + .then(() => + Promise.all( + _.map(_.values(id_maps.department), department_id => + sequelize.models.Department.find({ + where: { id: department_id } }) - ); - }); - }) + ) + ).then(departments_to_update => + Promise.all( + _.map(departments_to_update, department => { + department.manager_id = id_maps.user[department.manager_id] + return department.save() + }) + ) + ) + ) // insert leaves - .then(function(){ - return promise_to_restore_leaves({ - dump_json : dump_json, - id_maps : id_maps, - model : sequelize.models, - }); - }) - }, // End of restore_from_dump - }, - - instanceMethods : { - /* - * Return name suitable to use for precessing by machines, - * actually it just remove spaces and replace them with "_" - * - * */ - name_for_machine : function(){ - return this.name.replace(/\s+/, '_'); - }, - - reload_with_bank_holidays : function(){ - var self = this; - - return self.getBank_holidays() - .then(function(bank_holidays){ - self.bank_holidays = bank_holidays; - - return Promise.resolve(self); - }); - }, - - get_ldap_server : function(){ - - var config = this.get('ldap_auth_config'); - - // When testing consider using TEST LDAP server - // http://www.forumsys.com/en/tutorials/integration-how-to/ldap/online-ldap-test-server/ - var ldap = new LdapAuth({ - url : config.url, - bindDn : config.binddn, - bindCredentials : config.bindcredentials, - searchBase : config.searchbase, - searchFilter : '(mail={{username}})', - cache : false, - }); - - return ldap; - }, - - get_moment_to_datepicker_map : function() { - return { - "YYYY-MM-DD" : 'yyyy-mm-dd', - "YYYY/MM/DD" : 'yyyy/mm/dd', - "DD MMM, YY" : 'dd M, yy', - "DD/MM/YY" : "dd/mm/yy", - "DD/MM/YYYY" : "dd/mm/yyyy", - "MM/DD/YY" : 'mm/dd/yy', - }; - }, + .then(() => + promise_to_restore_leaves({ + dump_json, + id_maps, + model: sequelize.models + }) + ) + ) + } // End of restore_from_dump + + /* + * Return name suitable to use for precessing by machines, + * actually it just remove spaces and replace them with "_" + * + * */ + Company.prototype.name_for_machine = function () { + return this.name.replace(/\s+/g, '_') + } + + Company.prototype.reload_with_bank_holidays = function () { + const self = this + + return self.getBank_holidays().then(bank_holidays => { + self.bank_holidays = bank_holidays + + return Promise.resolve(self) + }) + } + + Company.prototype.get_ldap_server = function () { + const config = this.get('ldap_auth_config') + const tlsOptions = config.allow_unauthorized_cert + ? { rejectUnauthorized: false } + : {} + + // defaults + if (!config.searchfilter) config.searchfilter = '(mail={{username}})' + // When testing consider using TEST LDAP server + // http://www.forumsys.com/en/tutorials/integration-how-to/ldap/online-ldap-test-server/ + const ldap = new LdapAuth({ + url: config.url, + bindDn: config.binddn, + bindCredentials: config.bindcredentials, + searchBase: config.searchbase, + searchFilter: config.searchfilter, + cache: false, + tlsOptions + }) + + return ldap + } + + Company.prototype.get_moment_to_datepicker_map = function () { + return { + 'YYYY-MM-DD': 'yyyy-mm-dd', + 'YYYY/MM/DD': 'yyyy/mm/dd', + 'DD MMM, YY': 'dd M, yy', + 'DD/MM/YY': 'dd/mm/yy', + 'DD/MM/YYYY': 'dd/mm/yyyy', + 'MM/DD/YY': 'mm/dd/yy' + } + } - get_default_date_format : function() { - return this.getDataValue('date_format'); - }, + Company.prototype.get_default_date_format = function () { + return this.getDataValue('date_format') + } - get_available_date_formats : function() { - var obj = this.get_moment_to_datepicker_map(); - return _.keys( obj ); - }, + Company.prototype.get_available_date_formats = function () { + const obj = this.get_moment_to_datepicker_map() + return _.keys(obj) + } - get_default_date_format_for_date_picker : function() { - var self = this; + Company.prototype.get_default_date_format_for_date_picker = function () { + const self = this - var moment_to_datepicker_map = self.get_moment_to_datepicker_map(); + const moment_to_datepicker_map = self.get_moment_to_datepicker_map() - if ( moment_to_datepicker_map.hasOwnProperty( self.get_default_date_format() ) ) { - return moment_to_datepicker_map[ self.get_default_date_format() ]; - } + if ( + moment_to_datepicker_map.hasOwnProperty(self.get_default_date_format()) + ) { + return moment_to_datepicker_map[self.get_default_date_format()] + } - return 'yyyy-mm-dd'; - }, + return 'yyyy-mm-dd' + } + + // Takes date string in format specific for current company and produce string + // with date in generic format used internally within application + Company.prototype.normalise_date = function (date_str) { + console.log('Normalising date ' + date_str + ' for company ' + this.name) + return moment + .utc(date_str, this.get_default_date_format()) + .format('YYYY-MM-DD') + } + + // Returns moment UTC-ed object that takes into consideration company time zone + // (p to day's precision) + Company.prototype.get_today = function () { + // console.log('Getting today for company ' + this.name) + const self = this + + return moment.utc( + moment_tz + .utc() + .tz(self.timezone) + .format('YYYY-MM-DD') + ) + } + + Company.prototype.regenerateIntegrationApiToken = function () { + const self = this + + self.set('integration_api_token', uuidv4()) + + return self.save() + } + + // Promise schedule object valid for current company, if it does not have such + // in databse, retulr default one + Company.prototype.promise_schedule = function () { + const self = this + + return self.sequelize.models.Schedule.findOne({ + where: { company_id: self.id } + }).then(schedule => { + if (schedule) { + return Promise.resolve(schedule) + } - // Takes date string in format specific for current company and produce string - // with date in generic format used internally within application - normalise_date : function(date_str) { - return moment(date_str, this.get_default_date_format()).format('YYYY-MM-DD'); - }, + return self.sequelize.models.Schedule.promise_to_build_default_for({ + company_id: self.id + }) + }) + } - // Promise schedule object valid for current company, if it does not have such - // in databse, retulr default one - promise_schedule : function(){ - var self = this; + // lib/model/db/company.js - return self.sequelize.models.Schedule - .findOne({ - where : { company_id : self.id }, - }) - .then(function(schedule){ + Company.prototype.getDateFormat = function () { + return this.date_format || 'YYYY-MM-DD'; // Default format if not set + }; - if ( schedule) { - return Promise.resolve( schedule ); - } + // Return TRUE if company has restrictio on ly to show hollidays for its + // employees and prevent them from adding new ones + // + Company.prototype.is_mode_readonly_holidays = function () { + return this.mode === Company.get_mode_readonly_holidays() + } - return self.sequelize.models.Schedule - .promise_to_build_default_for({ company_id : self.id }); - }); - }, + Company.prototype.getSortedLeaveTypes = function () { + const self = this - } - }); - - return Company; -}; - - -function promise_to_restore_bank_holidays(args){ - var dump_json = args.dump_json, - model = args.model, - id_maps = args.id_maps; - - return model.BankHoliday.describe() - .then(function(bank_holiday_definition){ - // get array of JSON for each bank holiday - var bank_holiday_json = _.map(dump_json.bank_holidays,function(rec){ - var json = _.omit(rec, function(v,k,o){ - return ! bank_holiday_definition.hasOwnProperty(k); - }); - // substitude company IDs with fresh ones - json.companyId = id_maps.company[ json.companyId ]; - return json; - }); - - return Promise.all(_.map(bank_holiday_json, function(json){ - var old_id = json.id; - delete json.id; - - return model.BankHoliday.create(json) - .then(function(bh){ - id_maps.bank_holiday[ old_id ] = bh.id; - return Promise.resolve(1); - }); - })); - }); -}; - - -function promise_to_restore_leave_types(args){ - var dump_json = args.dump_json, - model = args.model, - id_maps = args.id_maps; - - return model.LeaveType.describe() - .then(function(leave_type_definition){ - var leave_type_json = _.map(dump_json.leave_types,function(rec){ - var json = _.omit(rec, function(v,k,o){ - return ! leave_type_definition.hasOwnProperty(k); - }); - // substitude company IDs with fresh ones - json.companyId = id_maps.company[ json.companyId ]; - return json; - }); - - return Promise.all(_.map(leave_type_json, function(json){ - var old_id = json.id; - delete json.id; - - return model.LeaveType.create(json) - .then(function(lt){ - id_maps.leave_type[ old_id ] = lt.id; - return Promise.resolve(1); - }); - })); - - }); -}; - -function promise_to_restore_departments (args){ - var dump_json = args.dump_json, - model = args.model, - id_maps = args.id_maps; - - return model.Department.describe() - .then(function(department_definition){ - var department_json = _.map(dump_json.departments,function(rec){ - var json = _.omit(rec, function(v,k,o){ - return ! department_definition.hasOwnProperty(k); - }); - - // substitude company IDs with fresh ones - json.companyId = id_maps.company[ json.companyId ]; - - return json; - }); - - return Promise.all(_.map(department_json, function(json){ - var old_id = json.id; - delete json.id; - - return model.Department.create(json) - .then(function(d){ - id_maps.department[ old_id ] = d.id; - return Promise.resolve(1); - }); - })); - - }); -}; - -function promise_to_restore_users(args) { - var dump_json = args.dump_json, - model = args.model, - id_maps = args.id_maps; - - return model.User.describe() - .then(function(user_definition){ - var user_json = _.map(dump_json.users, function(rec){ - var json = _.omit(rec, function(v,k,o){ - return ! user_definition.hasOwnProperty(k); - }); - - // substitude company IDs with fresh ones - json.companyId = id_maps.company[ json.companyId ]; - - // replace department ID with fresh one - json.DepartmentId = id_maps.department[ json.DepartmentId ]; - - // replace password hash - json.password = model.User.hashify_password('changeme'); - - return json; - }); - - return Promise.all(_.map(user_json, function(json){ - var old_id = json.id; - delete json.id; - - return model.User.create(json) - .then(function(u){ - id_maps.user[ old_id ] = u.id; - return Promise.resolve(1); - }); - })); - - }); -}; - -function promise_to_restore_leaves(args){ - var dump_json = args.dump_json, - model = args.model, - id_maps = args.id_maps; - - return model.Leave.describe() - .then(function(leave_definition){ - var leave_json = _.map( - _.flatten( - _.map(dump_json.users, function(u){ return u.my_leaves; }), - true - ), - function(rec){ - var json = _.omit(rec, function(v,k,o){ - return ! leave_definition.hasOwnProperty(k); - }); - - // replace approver ID with fresh one - json.approverId = id_maps.user[ json.approverId ]; - - // replace user ID with fresh one - json.userId = id_maps.user[ json.userId ]; - - // replace leave type ID with fresh one - json.leaveTypeId = id_maps.leave_type[ json.leaveTypeId ]; - - return json; - } - ); + const leaveTypes = self.leave_types || [] - return Promise.all(_.map(leave_json, function(json){ - var old_id = json.id; - delete json.id; + return leaveTypes.sort((a, b) => sorter(a.name, b.name)) + } - return model.Leave.create(json); - })); - }); -}; + return Company +} diff --git a/lib/model/db/department.js b/lib/model/db/department.js index 5b37afe1a..968bdf9e9 100644 --- a/lib/model/db/department.js +++ b/lib/model/db/department.js @@ -1,134 +1,280 @@ -"use strict"; +'use strict' -var moment = require('moment'), - _ = require('underscore'), - Promise = require('bluebird'), - CalendarMonth = require('../calendar_month'); +const moment = require('moment') +const _ = require('underscore') +const Promise = require('bluebird') +const Exception = require('../../error') +const CalendarMonth = require('../calendar_month') module.exports = function(sequelize, DataTypes) { - var Department = sequelize.define("Department", { - // TODO add validators! - name : { - type : DataTypes.STRING, - allowNull : false + const Department = sequelize.define( + 'Department', + { + // TODO add validators! + name: { + type: DataTypes.STRING, + allowNull: false + }, + allowance: { + type: DataTypes.FLOAT, + allowNull: false, + defaultValue: 20 + }, + personal: { + type: DataTypes.FLOAT, + allowNull: false, + defaultValue: 5 + }, + include_public_holidays: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true + }, + is_accrued_allowance: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + } + }, + { + underscored: true, + indexes: [ + { + fields: ['company_id'] }, - allowance : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : 20, - }, - include_public_holidays : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : true, + { + fields: ['id'] } - }, { - indexes : [ - { - fields : ['companyId'], - }, - { - fields : ['id'], - } - ], - classMethods: { - associate : function( models ) { - // We have constrains OFF as to prevent ORM complaining about - // cycle reference - Department.belongsTo(models.User, {as : 'boss', constraints: false}); - Department.hasMany(models.User, {as : 'users'}); - Department.belongsTo(models.Company, {as : 'company'}); - }, - - default_order_field : function(){ - return 'name'; - }, - }, + ] + } + ) + + Department.loadScope = function(models) { + Department.addScope('with_simple_users', { + include: [{ model: models.User, as: 'users' }] + }) + + Department.addScope('with_manager', { + include: [{ model: models.User, as: 'manager' }] + }) + + Department.addScope('with_supervisors', { + include: [{ model: models.User, as: 'supervisors' }] + }) + } + + Department.associate = function(models) { + // We have constrains OFF as to prevent ORM complaining about + // cycle reference + Department.belongsTo(models.User, { + as: 'manager', + constraints: false, + foreignKey: { name: 'manager_id' } + }) + Department.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: false } + }) + Department.hasMany(models.User, { + as: 'users', + foreignKey: { name: 'department_id', allowNull: false } + }) + + Department.hasMany(models.DepartmentSupervisor, { + as: 'supervisors_link', + foreignKey: { name: 'department_id', allowNull: false } + }) + + Department.belongsToMany(models.User, { + as: 'supervisors', + foreignKey: 'department_id', + otherKey: 'user_id', + through: models.DepartmentSupervisor + }) + } + + // added ref after def + Department.prototype.Model = Department + + Department.default_order_field = function() { + return 'name' + } + + // Return users related to current department and still active + Department.prototype.promise_active_users = function() { + return this.getUsers({ + where: sequelize.models.User.get_active_user_filter(), + scope: ['withDepartments'] + }) + } + + Department.prototype.promise_team_view_for_month = function(month) { + return this._promise_team_view({ start_date: month }) + } + + Department.prototype.promise_team_view_for_months_range = function( + start_month, + end_month + ) { + return this._promise_team_view({ + start_date: start_month, + end_date: end_month + }) + } + + Department.prototype._promise_team_view = function(args) { + const self = this + const model = sequelize.models + let start_date = args.start_date + let end_date = args.end_date + + const promise_users_and_leaves = Promise + + // First of all ensure that "start_date" is defined + .try(() => { + if (start_date) { + return Promise.resolve(start_date) + } + + return self + .getCompany() + .then(company => Promise.resolve((start_date = company.get_today()))) + }) - instanceMethods : { + // Ensure end_date is suitable if it was provided + .then(start_date => { + // If end_date was not provided: no need to validate it: set it to be equal to start date + if (!end_date) { + end_date = start_date + + return Promise.resolve() + } - // Return users related to current department and still active - promise_active_users : function(){ - return this.getUsers({ - where : sequelize.models.User.get_active_user_filter() - }); - }, + // If end date is provided... + // ... ensure start and end dates are from within same year + if ( + moment.utc(end_date).format('YYYY') !== + moment.utc(start_date).format('YYYY') + ) { + Exception.throw_user_error({ + user_error: 'Start and End dates should within single year', + system_error: + '_promise_team_view was called with start_date and end_date from different years.' + }) + } - promise_team_view : function(args){ + // ... ensure that start date proceed end date + if ( + moment.utc(start_date).dayOfYear() > moment.utc(end_date).dayOfYear() + ) { + Exception.throw_user_error({ + user_error: 'Start date needs to be before end date', + system_error: + '_promise_team_view was called with end_date prior to start_date' + }) + } - var self = this, - model = sequelize.models, - base_date = args.base_date || moment(); + return Promise.resolve() + }) - var promise_users_and_leaves = Promise.try(function(){ - return self.promise_active_users(); - }) - .then(function(users){ + // Get users + .then(() => self.promise_active_users()) + .then( + users => + Promise.all( + _.map(users, user => + user + .promise_my_leaves_for_calendar({ + year: start_date + }) + .then(leaves => { + const leave_days = _.flatten( + _.map(leaves, leave => + _.map(leave.get_days(), leave_day => { + leave_day.leave = leave + return leave_day + }) + ) + ) - return Promise.all( - _.map( - users, - function(user){ - return user.promise_my_leaves_for_calendar({ - year : base_date, + return user.promise_schedule_I_obey().then(schedule => + Promise.resolve({ + user, + leave_days, + schedule }) - .then(function(leaves){ - - var leave_days = _.flatten( _.map(leaves, function(leave){ - return _.map( leave.get_days(), function(leave_day){ - leave_day.leave = leave; - return leave_day; - }); - })); - - return user.promise_schedule_I_obey() - .then(function(schedule){ - return Promise.resolve({ - user : user, - leave_days : leave_days, - schedule : schedule, - }); - }); - }); - } - ) // End of map - ); // End of promise_users_and_leaves - }); - - var promise_company = self.getCompany({ - include:[ - { model : model.BankHoliday , as : 'bank_holidays' }, - { model : model.LeaveType , as : 'leave_types' }, - ] - }); - - return Promise.join( - promise_company, - promise_users_and_leaves, - function(company, users_and_leaves){ - _.each(users_and_leaves, function(user_data){ - var calendar_month = new CalendarMonth(base_date,{ - bank_holidays : - self.include_public_holidays - ? _.map( - company.bank_holidays, - function(day){return day.date} - ) - : [], - leave_days : user_data.leave_days, - schedule : user_data.schedule, - }); - - user_data.days = calendar_month.as_for_team_view(); - }); - - return Promise.resolve(users_and_leaves); + ) + }) + ) // End of map + ) // End of promise_users_and_leaves + ) + + const promise_company = self.getCompany({ + include: [ + { model: model.BankHoliday, as: 'bank_holidays' }, + { model: model.LeaveType, as: 'leave_types' } + ] + }) + + return Promise.join( + promise_company, + promise_users_and_leaves, + (company, users_and_leaves) => { + const number_of_months = + moment.utc(end_date).month() - moment.utc(start_date).month() + + users_and_leaves.forEach(user_data => { + user_data.days = [] + + // Now iterate throw all monthes between start and end dates + // and get calendar months for each + // and then combined them all togather + // filter bank holidays + const filteredBankHolidays = company.bank_holidays.map(day => ({ + date: day.date, + name: day.name + })) + + // console.log('Filtered Bank Holidays:', filteredBankHolidays) + + for (let i = 0; i <= number_of_months; i++) { + const calendar_month = new CalendarMonth( + moment + .utc(start_date) + .clone() + .add(i, 'months'), + { + bank_holidays: self.include_public_holidays + ? filteredBankHolidays + : [], + leave_days: user_data.leave_days, + schedule: user_data.schedule, + today: company.get_today(), + leave_types: company.leave_types } - ); + ) - }, // End of promise_team_view - } - }); + user_data.days.push(calendar_month.as_for_team_view()) + } // end of for + user_data.days = _.flatten(user_data.days) + }) + + return Promise.resolve(users_and_leaves) + } + ) + } // End of promise_team_view + + // Return new department object that is based on same ID but include all supervisors + Department.prototype.promise_me_with_supervisors = function() { + const self = this + + if (self.Model) { + return self.Model.scope('with_supervisors').findByPk(self.id) + } else { + // handle where undefined + return Promise.reject(new Error('Department.Model is not defined')) + } + } - return Department; -}; + return Department +} diff --git a/lib/model/db/department_supervisor.js b/lib/model/db/department_supervisor.js new file mode 100644 index 000000000..92d0e62a1 --- /dev/null +++ b/lib/model/db/department_supervisor.js @@ -0,0 +1,36 @@ +'use strict' + +module.exports = function(sequelize, DataTypes) { + const DepartmentSupervisor = sequelize.define( + 'DepartmentSupervisor', + {}, + { + underscored: true, + timestamps: true, + createdAt: 'created_at', + updatedAt: false, + indexes: [ + { + fields: ['department_id'] + }, + { + fields: ['user_id'] + } + ] + } + ) + + DepartmentSupervisor.associate = function(models) { + DepartmentSupervisor.belongsTo(models.Department, { + as: 'department', + foreignKey: 'department_id' + }) + + DepartmentSupervisor.belongsTo(models.User, { + as: 'user', + foreignKey: 'user_id' + }) + } + + return DepartmentSupervisor +} diff --git a/lib/model/db/email_audit.js b/lib/model/db/email_audit.js index 78c9a0280..b1d5705d8 100644 --- a/lib/model/db/email_audit.js +++ b/lib/model/db/email_audit.js @@ -1,62 +1,59 @@ +'use strict' -"use strict"; +const htmlToText = require('html-to-text') -var - Bluebird = require('bluebird'), - htmlToText = require('html-to-text'), - moment = require("moment"); - -module.exports = function(sequelize, DataTypes){ - var EmailAudit = sequelize.define("EmailAudit", { - email : { - type : DataTypes.STRING, - allowNull : false, - }, - subject : { - type : DataTypes.TEXT, - allowNull : false, - }, - body : { - type : DataTypes.TEXT, - allowNull : false, - }, - },{ - underscored : true, - freezeTableName : true, - timestamps : true, - createdAt : 'created_at', - updatedAt : false, - indexes : [{ - fields : [ 'created_at' ], - },{ - fields : [ 'user_id' ], - }], - - classMethods : { - associate : function(models) { - - EmailAudit.belongsTo(models.Company, { - as : 'company', - foreignKey : 'company_id', - }); - - - EmailAudit.belongsTo(models.User, { - as : 'user', - foreignKey : 'user_id', - }); +module.exports = function(sequelize, DataTypes) { + const EmailAudit = sequelize.define( + 'EmailAudit', + { + email: { + type: DataTypes.STRING, + allowNull: false }, - }, - - instanceMethods : { - // Present the email body as a text, strips off any html tags etc - // - body_as_text : function(){ - return this.body.indexOf('DOCTYPE') > 0 ? htmlToText.fromString(this.body) : this.body; + subject: { + type: DataTypes.TEXT, + allowNull: false }, + body: { + type: DataTypes.TEXT, + allowNull: false + } }, - - }); - - return EmailAudit; -}; + { + underscored: true, + timestamps: true, + createdAt: 'created_at', + updatedAt: false, + indexes: [ + { + fields: ['created_at'] + }, + { + fields: ['user_id'] + } + ] + } + ) + + EmailAudit.associate = function(models) { + EmailAudit.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: false } + }) + + EmailAudit.belongsTo(models.User, { + as: 'user', + foreignKey: { name: 'user_id', allowNull: false } + }) + } + + // Present the email body as a text, strips off any html tags etc + // + EmailAudit.prototype.body_as_text = function() { + return this.body.indexOf('DOCTYPE') > 0 + ? htmlToText.fromString(this.body) + : this.body + } + + return EmailAudit +} diff --git a/lib/model/db/index.js b/lib/model/db/index.js index 0ad2e9227..6aee6eb1c 100644 --- a/lib/model/db/index.js +++ b/lib/model/db/index.js @@ -1,31 +1,74 @@ -"use strict"; - -var fs = require("fs"); -var path = require("path"); -var Sequelize = require("sequelize"); -var env = process.env.NODE_ENV || "development"; -var config = require(__dirname + '/../../../config/db.json')[env]; -var sequelize = new Sequelize(config.database, config.username, config.password, config); -var db = {}; - -fs - .readdirSync(__dirname) - .filter(function(file) { - return (file.indexOf(".") !== 0) - && (file !== "index.js"); - }) - .forEach(function(file) { - var model = sequelize["import"](path.join(__dirname, file)); - db[model.name] = model; - }); - -Object.keys(db).forEach(function(modelName) { - if ("associate" in db[modelName]) { - db[modelName].associate(db); - } -}); - -db.sequelize = sequelize; -db.Sequelize = Sequelize; - -module.exports = db; +'use strict' + +const fs = require('fs') +const path = require('path') +const Sequelize = require('sequelize') +const env = process.env.NODE_ENV || 'development' + +// Extract the useSSL flag from an environment variable +const useSSL = process.env.USE_SSL === 'true' +console.log('useSSL is:', useSSL) + +const databaseConfig = { + dialectOptions: {}, + logging: (process.env.DB_LOGGING === 'true' && console.log) || false, + pool: { + max: parseInt(process.env.DB_POOL_MAX) || 5, + min: parseInt(process.env.DB_POOL_MIN) || 0, + acquire: parseInt(process.env.DB_POOL_ACQUIRE) || 60000, + idle: parseInt(process.env.DB_POOL_IDLE) || 10000 + } +} + +// If SSL is required, configure dialectOptions for SSL +if (useSSL) { + databaseConfig.dialectOptions.ssl = { + require: process.env.DB_SSL_REQUIRE === 'true', + rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED !== 'false' // Defaults to true unless explicitly set to 'false' + } +} + +// Initialize Sequelize with DATABASE_URL and the configuration +const sequelize = new Sequelize(process.env.DATABASE_URL, databaseConfig) + +console.log('DB Config:', databaseConfig) +console.log('DB URL:', process.env.DATABASE_URL) + +// Example of defining models +const db = {} + +fs.readdirSync(__dirname) + .filter(file => file.indexOf('.') !== 0 && file !== 'index.js') + .forEach(file => { + const model = sequelize.import(path.join(__dirname, file)) + db[model.name] = model + }) + +// Link models according associations +// +Object.keys(db).forEach(modelName => { + if ('associate' in db[modelName]) { + db[modelName].associate(db) + } +}) + +// Add scopes +// +Object.keys(db).forEach(modelName => { + if ('loadScope' in db[modelName]) { + db[modelName].loadScope(db) + } +}) + +// Link models based on associations that are based on scopes +// +Object.keys(db).forEach(modelName => { + if ('scopeAssociate' in db[modelName]) { + db[modelName].scopeAssociate(db) + } +}) + +db.sequelize = sequelize +db.Sequelize = Sequelize + +module.exports = db diff --git a/lib/model/db/leave.js b/lib/model/db/leave.js index c9fea954d..e18260d1e 100644 --- a/lib/model/db/leave.js +++ b/lib/model/db/leave.js @@ -1,438 +1,546 @@ +'use strict' -"use strict"; - -var - _ = require('underscore'), - moment = require('moment'), - Promise = require("bluebird"), - LeaveDay = require('../leave_day'); +const _ = require('underscore') +const moment = require('moment') +const Promise = require('bluebird') +const LeaveDay = require('../leave_day') module.exports = function(sequelize, DataTypes) { - var Leave = sequelize.define("Leave", { - // TODO add validators! - 'status' : { - type : DataTypes.INTEGER, - allowNull : false - }, - employee_comment : { - type : DataTypes.STRING, - allowNull : true, - }, - approver_comment : { - type : DataTypes.STRING, - allowNull : true, + const Leave = sequelize.define( + 'Leave', + { + // TODO add validators! + status: { + type: DataTypes.INTEGER, + allowNull: false + }, + employee_comment: { + type: DataTypes.TEXT, + allowNull: true + }, + approver_comment: { + type: DataTypes.TEXT, + allowNull: true + }, + decided_at: { + type: DataTypes.DATE, + allowNull: true + }, + + date_start: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: sequelize.NOW + }, + day_part_start: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1 // VPP TODO replace with constant value + }, + date_end: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: sequelize.NOW + }, + day_part_end: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 1 // VPP TODO replace with constant value + } + }, + { + underscored: true, + indexes: [ + { + fields: ['user_id'] }, - decided_at : { - type : DataTypes.DATE, - allowNull : true, + { + fields: ['leave_type_id'] }, + { + fields: ['approver_id'] + } + ] + } + ) - date_start : { - type : DataTypes.DATE, - allowNull : false, - defaultValue : sequelize.NOW, - }, - day_part_start : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : 1, // VPP TODO replace with constant value - }, - date_end : { - type : DataTypes.DATE, - allowNull : false, - defaultValue : sequelize.NOW, - }, - day_part_end : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : 1, // VPP TODO replace with constant value - }, - }, { + Leave.associate = function(models) { + Leave.belongsTo(models.User, { + as: 'user', + foreignKey: { name: 'user_id', allowNull: false } + }) + Leave.belongsTo(models.User, { + as: 'approver', + foreignKey: 'approver_id' + }) + Leave.belongsTo(models.LeaveType, { + as: 'leave_type', + foreignKey: { name: 'leave_type_id', allowNull: false } + }) + Leave.hasMany(models.Comment, { + as: 'comments', + foreignKey: 'company_id', + scope: { + entity_type: models.Comment.getEntityTypeLeave() + } + }) + } - indexes : [ - { - fields : ['userId'], - }, - { - fields : ['leaveTypeId'], - }, - { - fields : ['approverId'], - }, - ], - classMethods : { - associate : function( models ){ - Leave.belongsTo(models.User, { as : 'user',foreignKey : 'userId' }); - Leave.belongsTo(models.User, { as : 'approver',foreignKey : 'approverId' }); - Leave.belongsTo(models.LeaveType, { as : 'leave_type' } ); - }, - - status_new : function(){ - return 1; - }, - - status_approved : function(){ - return 2; - }, - - status_rejected : function() { - return 3; - }, - - status_pended_revoke : function(){ - return 4; - }, - - status_canceled : function() { - return 5; - }, - - - leave_day_part_all : function(){ - return 1; - }, - - leave_day_part_morning : function(){ - return 2; - }, - - leave_day_part_afternoon : function(){ - return 3; - }, - - /* - * Create new leave for provided parameters. - * Returns promise that is resolved with newly created leave row - * */ - create_new_leave : function(args){ - - // Make sure all required data is provided - _.each( - ['for_employee','of_type','with_parameters'], - function(property){ - if (! _.has(args, property)) { - throw new Error('No mandatory '+property+' was provided'); - } - } - ); - - var employee = args.for_employee, - leave_type = args.of_type, - valide_attributes = args.with_parameters; - - - // Make sure that booking to be created is not going to ovelap with - // any existing bookings - return Promise.try(function(){ - return employee.validate_overlapping(valide_attributes); - }) - .then(function(){ - return employee.promise_superviser(); - }) - - .then(function(superviser){ - - var start_date = moment(valide_attributes.from_date), - end_date = moment(valide_attributes.to_date); - - // Check that start date is not bigger then end one - if ( start_date.toDate() > end_date.toDate() ) { - throw new Error('Start date is later than end date'); - } - - var new_leave_status = employee.is_auto_approve() - ? Leave.status_approved() - : Leave.status_new(); - - // Following statement creates in memory only leave object - // it is not in database until .save() method is called - var leave_to_create = sequelize.models.Leave.build({ - userId : employee.id, - leaveTypeId : leave_type.id, - status : new_leave_status, - approverId : superviser.id, - employee_comment : valide_attributes.reason, - - date_start : start_date.format('YYYY-MM-DD'), - date_end : end_date.format('YYYY-MM-DD'), - day_part_start : valide_attributes.from_date_part, - day_part_end : valide_attributes.to_date_part, - }); - - return employee.validate_leave_fits_into_remaining_allowance({ - year : start_date, - leave_type : leave_type, - leave : leave_to_create, - }) - .then(function(){ - return leave_to_create.save(); - }); - - - }); - }, // End of create_new_leave - }, // End of class methods - - instanceMethods : { - -get_days : function() { - - var self = this, - start_date = moment(this.date_start), - end_date = moment(this.date_end), - days = [ start_date ]; - - if (self.hasOwnProperty('_days')) { - return self._days; + Leave.status_new = function() { + return 1 + } + + Leave.status_approved = function() { + return 2 + } + + Leave.status_rejected = function() { + return 3 } - if ( ! start_date.isSame( end_date, 'day') ){ + Leave.status_pended_revoke = function() { + return 4 + } + + Leave.status_canceled = function() { + return 5 + } - var days_in_between = end_date.diff( start_date, 'days' ) - 1; + Leave.leave_day_part_all = function() { + return 1 + } - for (var i=1; i<=days_in_between; i++) { - days.push( start_date.clone().add(i, 'days') ); + Leave.leave_day_part_morning = function() { + return 2 + } + + Leave.leave_day_part_afternoon = function() { + return 3 + } + + Leave.does_skip_approval = function(user, leave_type) { + return user.is_auto_approve() || leave_type.is_auto_approve() + } + + Leave.does_use_personal = function(leave_type) { + return leave_type.does_use_personal() + } + + /* + * Create new leave for provided parameters. + * Returns promise that is resolved with newly created leave row + * */ + Leave.create_new_leave = function(args) { + // Make sure all required data is provided + _.each(['for_employee', 'of_type', 'with_parameters'], property => { + if (!_.has(args, property)) { + throw new Error('No mandatory ' + property + ' was provided') } + }) - days.push( end_date ); + const employee = args.for_employee + const leave_type = args.of_type + const valide_attributes = args.with_parameters + + // Make sure that booking to be created is not going to ovelap with + // any existing bookings + return Promise.try(() => employee.validate_overlapping(valide_attributes)) + .then(() => employee.promise_manager()) + .then(main_supervisor => { + const start_date = moment + .utc(valide_attributes.from_date) + .format('YYYY-MM-DD') + const end_date = moment + .utc(valide_attributes.to_date) + .format('YYYY-MM-DD') + + // Check that start date is not bigger then end one + if (start_date.toDate() > end_date.toDate()) { + throw new Error('Start date is later than end date') + } + + const new_leave_status = employee.is_auto_approve() + ? Leave.status_approved() + : Leave.status_new() + + // Following statement creates in memory only leave object + // it is not in database until .save() method is called + const leave_to_create = sequelize.models.Leave.build({ + user_id: employee.id, + leave_type_id: leave_type.id, + status: new_leave_status, + approver_id: main_supervisor.id, + employee_comment: valide_attributes.reason, + + date_start: start_date.format('YYYY-MM-DD'), + date_end: end_date.format('YYYY-MM-DD'), + day_part_start: valide_attributes.from_date_part, + day_part_end: valide_attributes.to_date_part + }) + + return employee + .validate_leave_fits_into_remaining_allowance({ + year: start_date, + leave_type, + leave: leave_to_create + }) + .then(() => leave_to_create.save()) + }) + } // End of create_new_leave + + Leave.prototype.reloadWithAssociates = function() { + const self = this + + return self.reload({ + include: [ + { model: self.sequelize.models.User, as: 'user' }, + { model: self.sequelize.models.User, as: 'approver' }, + { model: self.sequelize.models.LeaveType, as: 'leave_type' } + ] + }) } - days = _.map( - days, - function(day){ - return new LeaveDay({ - sequelize : sequelize, - date : day.format('YYYY-MM-DD'), - day_part : day.isSame(start_date, 'day') - ? self.day_part_start - : day.isSame(end_date, 'day') - ? self.day_part_end - : Leave.leave_day_part_all(), - }); + Leave.prototype.get_days = function() { + if (this._days) { + return this._days + } + + const start_date = moment.utc(this.date_start).format('YYYY-MM-DD') + const end_date = moment.utc(this.date_end).format('YYYY-MM-DD') + const days = [start_date] + + // console.log("db.leave start_date", start_date); + + if (start_date !== end_date) { + // Use the formatted strings for comparison + const days_in_between = + moment.utc(end_date).diff(moment.utc(start_date), 'days') - 1 + + for (let i = 1; i <= days_in_between; i++) { + days.push( + moment + .utc(start_date) + .add(i, 'days') + .format('YYYY-MM-DD') + ) } - ); - return self._days = days; -}, + days.push(end_date) + } -fit_with_leave_request : function(leave_request) { + // Create LeaveDay objects + this._days = days.map( + day => + new LeaveDay({ + leave_type_id: this.leave_type_id, + sequelize, + date: day, // day is already a formatted string + day_part: + day === start_date + ? this.day_part_start + : day === end_date + ? this.day_part_end + : Leave.leave_day_part_all() + }) + ) + + return this._days + } + Leave.prototype.fit_with_leave_request = function(leave_request) { // If start and end dates are the same, check if one of them fit // into fist or last leave_days. if ( - leave_request.is_within_one_day() && ( - leave_request.does_fit_with_leave_day( _.last(this.get_days()) ) - || - leave_request.does_fit_with_leave_day( _.first(this.get_days()) ) - ) - ) { - return true; + leave_request.is_within_one_day() && + (leave_request.does_fit_with_leave_day(_.last(this.get_days())) || + leave_request.does_fit_with_leave_day(_.first(this.get_days()))) + ) { + return true } // If start and end dates are different, check if start date // fits into end leave_day or end date fits int start leave_date. if ( - (! leave_request.is_within_one_day()) && ( - leave_request.does_fit_with_leave_day_at_start( - _.last(this.get_days()) - ) - || - leave_request.does_fit_with_leave_day_at_end( - _.first(this.get_days()) - ) - ) + !leave_request.is_within_one_day() && + (leave_request.does_fit_with_leave_day_at_start( + _.last(this.get_days()) + ) || + leave_request.does_fit_with_leave_day_at_end(_.first(this.get_days()))) ) { - return true; + return true } - return false; -}, // End of fit_with_leave_request - -is_new_leave : function() { - return this.status === Leave.status_new(); -}, + return false + } // End of fit_with_leave_request -is_pended_revoke_leave : function(){ - return this.status === Leave.status_pended_revoke(); -}, - -// Leave is treated as "approved" one if it is in approved staus -// or if it is waiting decision on revoke action -// -is_approved_leave : function() { - return this.status === Leave.status_approved() || - this.status === Leave.status_pended_revoke() ; -}, + Leave.prototype.is_new_leave = function() { + return this.status === Leave.status_new() + } -get_start_leave_day : function(){ - return this.get_days()[0]; -}, + Leave.prototype.is_pended_revoke_leave = function() { + return this.status === Leave.status_pended_revoke() + } -get_end_leave_day : function(){ - return this.get_days()[ this.get_days().length - 1 ]; -}, + // Leave is treated as "approved" one if it is in approved staus + // or if it is waiting decision on revoke action + // + Leave.prototype.is_approved_leave = function() { + return ( + this.status === Leave.status_approved() || + this.status === Leave.status_pended_revoke() + ) + } -get_deducted_days_number : function(args) { - var number_of_days = this.get_deducted_days(args).length; + Leave.prototype.is_auto_approve = function() { + return Leave.does_skip_approval(this.user, this.leave_type) + } - // leave spans via on working day only, pay attention only to the start date - if (number_of_days === 1 && !this.get_start_leave_day().is_all_day_leave()) { - number_of_days = number_of_days - 0.5; + Leave.does_use_personal = function() { + return Leave.does_use_personal(this.user, this.leave_type) } - // case when leave spreads for more then one day, then check if both start and day - // are halfs - else if (number_of_days > 1) { - if ( ! this.get_start_leave_day().is_all_day_leave() ){ - number_of_days = number_of_days - 0.5; - } - if ( ! this.get_end_leave_day().is_all_day_leave() ) { - number_of_days = number_of_days - 0.5; - } + // Determine if leave starts with half day in the morning + // + Leave.prototype.does_start_half_morning = function() { + return this.day_part_start === Leave.leave_day_part_morning() } - return number_of_days; -}, + Leave.prototype.does_start_half_afternoon = function() { + return this.day_part_start === Leave.leave_day_part_afternoon() + } -get_deducted_days : function(args) { + // Determine if leave ends with half a day in the afternoon + // + Leave.prototype.does_end_half_afternoon = function() { + return this.day_part_end === Leave.leave_day_part_afternoon() + } - var leave_days = [], - ignore_allowance = false, - leave_type = this.leave_type || args.leave_type, - year; + Leave.prototype.does_end_half_morning = function() { + return this.day_part_end === Leave.leave_day_part_morning() + } - if (args && args.hasOwnProperty('ignore_allowance')) { - ignore_allowance = args.ignore_allowance; + Leave.prototype.get_start_leave_day = function() { + return this.get_days()[0] } - if (args && args.hasOwnProperty('year')) { - year = moment(args.year, 'YYYY'); + Leave.prototype.get_end_leave_day = function() { + return this.get_days()[this.get_days().length - 1] } - // If current Leave stands for type that does not use - // allowance, ignore rest of the code; - if (! ignore_allowance && !leave_type.use_allowance) return leave_days; + Leave.prototype.get_deducted_days_number = function(args) { + let number_of_days = this.get_deducted_days(args).length + + // leave spans via on working day only, pay attention only to the start date + if ( + number_of_days === 1 && + !this.get_start_leave_day().is_all_day_leave() + ) { + number_of_days = number_of_days - 0.5 + } - var user = this.user || this.approver || args.user; + // case when leave spreads for more then one day, then check if both start and day + // are halfs + else if (number_of_days > 1) { + if (!this.get_start_leave_day().is_all_day_leave()) { + number_of_days = number_of_days - 0.5 + } + if (!this.get_end_leave_day().is_all_day_leave()) { + number_of_days = number_of_days - 0.5 + } + } - var bank_holiday_map = {}; + return number_of_days + } - user.company.bank_holidays.forEach(function(bank_holiday){ - bank_holiday_map[ bank_holiday.get_pretty_date() ] = 1; - }); + Leave.prototype.get_deducted_days = function(args) { + let leave_days = [] + let ignore_allowance = false + const leave_type = this.get('leave_type') || args.leave_type + let year - // Because we currently in synchronos code we have to rely on cahed value - // rather then fetching it here, and prey that whoever called current - // method made sure that the cach is populated - var schedule = user.cached_schedule; + if (args && args.ignore_allowance) { + ignore_allowance = args.ignore_allowance + } - leave_days = _.filter( - _.map(this.get_days(), function(leave_day){ + if (args && args.year) { + year = moment.utc(args.year, 'YYYY') + } - // Ignore bank holidays - if ( bank_holiday_map[ leave_day.get_pretty_date() ] ) return; + // If current Leave stands for type that does not use + // allowance, ignore rest of the code; + if (!ignore_allowance && !leave_type.use_allowance) return leave_days - // If it happenned that current leave day is from the year current - // call was made of, ignore that day - if (year && year.year() !== moment(leave_day.date).year()) return; + const user = this.user || this.approver || args.user - // Ignore non-working days (weekends) - if ( ! schedule.is_it_working_day({ day : moment(leave_day.date) }) ){ - return; - } + // added 240513 from old repo + const includePublicHolidays = user.department + ? user.department.include_public_holidays + : true - return leave_day; - }), - function(leave_day){ - return !! leave_day; + const bank_holiday_map = {} + + // added 240513 from old repo + if (includePublicHolidays) { + user.company.bank_holidays.forEach(bh => { + bank_holiday_map[bh.get_pretty_date()] = 1 + }) } - ) || []; - - return leave_days; -}, // End get_deducted_days - -promise_to_reject : function() { - // See explanation to promise_to_approve - this.status = this.is_pended_revoke_leave() ? - Leave.status_approved(): - Leave.status_rejected(); - return this.save(); -}, - -promise_to_approve : function() { - // If current leave is one with requested revoke, then - // approve action set it into Rejected status - // otherwise it is approve action for new leave - // so put leave into Approved - this.status = this.is_pended_revoke_leave() ? - Leave.status_rejected(): - Leave.status_approved(); - return this.save(); -}, - -promise_to_revoke : function(){ - var self = this; - - return self.getUser({ - include : [ - { - model : sequelize.models.Department, - as : 'department', - } - ], + + user.company.bank_holidays.forEach(bank_holiday => { + bank_holiday_map[bank_holiday.get_pretty_date()] = 1 }) - .then(function(user){ - var new_leave_status = user.is_auto_approve() - ? Leave.status_rejected() - : Leave.status_pended_revoke(); + // Because we currently in synchronos code we have to rely on cahed value + // rather then fetching it here, and prey that whoever called current + // method made sure that the cach is populated + const schedule = user.cached_schedule + + leave_days = + _.filter( + _.map(this.get_days(), leave_day => { + // Ignore bank holidays // updated 240513 with code from old repo + if ( + includePublicHolidays && + bank_holiday_map[leave_day.get_pretty_date()] + ) + return + + // If it happenned that current leave day is from the year current + // call was made of, ignore that day + if (year && year.year() !== moment.utc(leave_day.date).year()) return + + // Ignore non-working days (weekends) + if ( + !schedule.is_it_working_day({ + day: moment.utc(leave_day.date) + }) + ) { + return + } + + return leave_day + }), + leave_day => !!leave_day + ) || [] + + return leave_days + } // End get_deducted_days + + Leave.prototype.promise_to_reject = function(args) { + const self = this + + if (!args) { + args = {} + } - self.approverId = user.department.bossId; - self.status = new_leave_status; + if (!args.by_user) { + throw new Error('promise_to_reject has to have by_user parameter') + } - return self.save(); - }) -}, + const by_user = args.by_user -promise_to_cancel : function(){ - var self = this; + // See explanation to promise_to_approve + self.status = self.is_pended_revoke_leave() + ? Leave.status_approved() + : Leave.status_rejected() - if ( ! self.is_new_leave() ) { - throw new Error('An attempt to cancel non-new leave request id : '+self.id); + self.approver_id = by_user.id + + return self.save() } - self.status = Leave.status_canceled(); + Leave.prototype.promise_to_approve = function(args) { + const self = this - return self.save(); -}, + if (!args) { + args = {} + } -get_leave_type_name : function() { - var leave_type = this.get('leave_type'); + if (!args.by_user) { + throw new Error('promise_to_approve has to have by_user parameter') + } - if (! leave_type ) { - return ''; - } else { - return leave_type.name; + const by_user = args.by_user + + // If current leave is one with requested revoke, then + // approve action set it into Rejected status + // otherwise it is approve action for new leave + // so put leave into Approved + self.status = self.is_pended_revoke_leave() + ? Leave.status_rejected() + : Leave.status_approved() + + self.approver_id = by_user.id + + return self.save() } -}, -promise_approver : function() { + Leave.prototype.promise_to_revoke = function(args) { + const self = this - return this.getApprover({ - include : [{ - model : sequelize.models.Company, - as : 'company', - include : [{ - model : sequelize.models.BankHoliday, - as : 'bank_holidays', - }], - }], - }) -}, + return self + .getUser({ + include: [ + { + model: sequelize.models.Department, + as: 'department' + } + ] + }) + .then(user => { + const new_leave_status = user.is_auto_approve() + ? Leave.status_rejected() + : Leave.status_pended_revoke() + + // By default it is user main manager is one who has to approve the revoked request + self.approver_id = user.department.manager_id + + self.status = new_leave_status + + return self.save() + }) + } + Leave.prototype.promise_to_cancel = function() { + const self = this - }, - }); + if (!self.is_new_leave()) { + throw new Error( + 'An attempt to cancel non-new leave request id : ' + self.id + ) + } + + self.status = Leave.status_canceled() + + return self.save() + } + + Leave.prototype.get_leave_type_name = function() { + const leave_type = this.get('leave_type') + + if (!leave_type) { + return '' + } else { + return leave_type.name + } + } + + Leave.prototype.promise_approver = function() { + return this.getApprover({ + include: [ + { + model: sequelize.models.Company, + as: 'company', + include: [ + { + model: sequelize.models.BankHoliday, + as: 'bank_holidays' + } + ] + } + ] + }) + } - return Leave; -}; + return Leave +} diff --git a/lib/model/db/leave_type.js b/lib/model/db/leave_type.js index 6d900c4fc..575d0f4cc 100644 --- a/lib/model/db/leave_type.js +++ b/lib/model/db/leave_type.js @@ -1,57 +1,109 @@ -"use strict"; +'use strict' module.exports = function(sequelize, DataTypes) { - var LeaveType = sequelize.define("LeaveType", { - // TODO add validators! - name : { - type : DataTypes.STRING, - allowNull : false - }, - color : { - type : DataTypes.STRING, - allowNull : false, - defaultValue : '#ffffff', - }, - use_allowance : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : true, - }, - limit : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : 0, - }, - }, { - classMethods: { - associate : function( models ) { - LeaveType.belongsTo(models.Company, {as : 'company'}); - LeaveType.hasMany(models.Leave, {as : 'leaves', foreignKey : 'leaveTypeId'}); - }, - - generate_leave_types : function(args){ - var company = args.company; - - return LeaveType.bulkCreate([ - { - name : 'Holiday', - color : '#22AA66', - companyId : company.id, - }, - { - name : 'Sick Leave', - color : '#459FF3', - companyId : company.id, - limit : 10, - }, - ]) - }, - }, - - instanceMethods : { - - } - }); - - return LeaveType; -}; + const LeaveType = sequelize.define( + 'LeaveType', + { + // TODO add validators! + name: { + type: DataTypes.STRING, + allowNull: false + }, + color: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: '#ffffff' + }, + use_allowance: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true + }, + use_personal: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + limit: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0 + }, + sort_order: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: 'Is used to determine sorting order of leave types' + }, + auto_approve: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'If true, leave requests of this type will be auto-approved' + }, + manager_only: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'If true, this leave type can only be used by managers/supervisors' + }, + private_leave: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'If true, this leave type is private and only visible to the employee or a manager' + } + }, + { + underscored: true + } + ) + + LeaveType.associate = function(models) { + LeaveType.belongsTo(models.Company, { + as: 'company', + foreignKey: 'company_id' + }) + LeaveType.hasMany(models.Leave, { + as: 'leaves', + foreignKey: { name: 'leave_type_id', allowNull: false } + }) + } + + LeaveType.generate_leave_types = function(args) { + const company = args.company + + return LeaveType.bulkCreate([ + { + name: 'Holiday', + color: '#22AA66', + company_id: company.id + }, + { + name: 'Sick Leave', + color: '#459FF3', + company_id: company.id, + limit: 10, + use_allowance: 0 + } + ]) + } + + LeaveType.prototype.get_color_value = function() { + return this.color || '#ffffff' + } + + LeaveType.prototype.is_auto_approve = function() { + return this.auto_approve === true + } + + LeaveType.prototype.does_use_personal = function() { + return this.does_use_personal === true + } + + LeaveType.prototype.is_private_leave = function() { + return this.private_leave || false + } + + return LeaveType +} diff --git a/lib/model/db/schedule.js b/lib/model/db/schedule.js index 3e38f55eb..947f26a02 100644 --- a/lib/model/db/schedule.js +++ b/lib/model/db/schedule.js @@ -1,158 +1,182 @@ +'use strict' -'use strict'; +const moment = require('moment') +const Promise = require('bluebird') -var moment = require('moment'), - Promise = require("bluebird"); - -function works_whole_day() { return 1 } -function works_none() { return 2 } -function works_morning() { return 3 } -function works_afternoon() { return 4 } +function works_whole_day() { + return 1 +} +function works_none() { + return 2 +} +function works_morning() { + return 3 +} +function works_afternoon() { + return 4 +} function week_day_flag_setter(flag_name) { - return function(v){ this.setDataValue(flag_name, v ? works_whole_day() : works_none()) }; + return function(v) { + this.setDataValue(flag_name, v ? works_whole_day() : works_none()) + } } -module.exports = function(sequelize, DataTypes){ - - var Schedule = sequelize.define("Schedule", { - monday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_whole_day(), - set : week_day_flag_setter('monday'), - }, - tuesday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_whole_day(), - set : week_day_flag_setter('tuesday'), - }, - wednesday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_whole_day(), - set : week_day_flag_setter('wednesday'), - }, - thursday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_whole_day(), - set : week_day_flag_setter('thursday'), - }, - friday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_whole_day(), - set : week_day_flag_setter('friday'), - }, - saturday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_none(), - set : week_day_flag_setter('saturday'), - }, - sunday : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : works_none(), - set : week_day_flag_setter('sunday'), - }, - },{ - - underscored : true, - freezeTableName : true, - tableName : 'schedule', - - indexes : [ - { fields : ['user_id'] }, - { fields : ['company_id'] }, - ], - - classMethods: { - associate : function( models ) { - Schedule.belongsTo(models.Company, {as : 'company', foreignKey : 'company_id'}); - Schedule.belongsTo(models.User, {as : 'user', foreignKey : 'user_id'}); - }, - - promise_to_build_default_for : function(args){ - var company_id = args.company_id, - user_id = args.user_id; - - if ( ! company_id && ! user_id ) { - throw new Error('Needs to have either company_id or user_id'); - } - - var default_schedule = sequelize.models.Schedule.build({ - company_id : company_id, - user_id : user_id, - }); - - return Promise.resolve(default_schedule); - }, - }, - - validate : { - relatesToEitherUserOrCompanyButNotBoth : function(){ - if ( this.company_id && this.user_id ) { - console.error('company_id='+this.company_id+', user_id='+this.user_id); - throw new Error('Schedule should be connected either to company of to user but not to both'); - } +module.exports = function(sequelize, DataTypes) { + const Schedule = sequelize.define( + 'Schedule', + { + monday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_whole_day(), + set: week_day_flag_setter('monday') }, - - relatesToUserOrCompany : function(){ - if ( ! this.company_id && ! this.user_id ){ - console.error('company_id='+this.company_id+', user_id='+this.user_id); - throw new Error('Schedule needs to be related to eaither company or user'); - } + tuesday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_whole_day(), + set: week_day_flag_setter('tuesday') }, - }, - - instanceMethods : { - is_user_specific : function() { - return !! this.user_id; + wednesday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_whole_day(), + set: week_day_flag_setter('wednesday') }, - - is_it_working_day : function(args){ - var day = args.day; - - if ( ! day ) { - throw new Error('"is_it_working_day" requires to have "day" parameter'); - } - - return this[ moment(day).format('dddd').toLowerCase() ] === works_whole_day(); - }, - - works_monday : function(){ - return this.monday === works_whole_day(); + thursday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_whole_day(), + set: week_day_flag_setter('thursday') }, - - works_tuesday : function(){ - return this.tuesday === works_whole_day(); + friday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_whole_day(), + set: week_day_flag_setter('friday') }, - - works_wednesday : function(){ - return this.wednesday === works_whole_day(); + saturday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_none(), + set: week_day_flag_setter('saturday') }, - - works_thursday : function(){ - return this.thursday === works_whole_day(); - }, - - works_friday : function(){ - return this.friday === works_whole_day(); - }, - - works_saturday : function(){ - return this.saturday === works_whole_day(); - }, - - works_sunday : function(){ - return this.sunday === works_whole_day(); - }, - + sunday: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: works_none(), + set: week_day_flag_setter('sunday') + } }, - }); - - return Schedule; -}; + { + underscored: true, + freezeTableName: true, + tableName: 'schedules', + + indexes: [{ fields: ['user_id'] }, { fields: ['company_id'] }], + + validate: { + relatesToEitherUserOrCompanyButNotBoth: function() { + if (this.company_id && this.user_id) { + console.error( + 'company_id=' + this.company_id + ', user_id=' + this.user_id + ) + throw new Error( + 'Schedule should be connected either to company of to user but not to both' + ) + } + }, + + relatesToUserOrCompany: function() { + if (!this.company_id && !this.user_id) { + console.error( + 'company_id=' + this.company_id + ', user_id=' + this.user_id + ) + throw new Error( + 'Schedule needs to be related to eaither company or user' + ) + } + } + } + } + ) + + Schedule.associate = function(models) { + Schedule.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: true } + }) + Schedule.belongsTo(models.User, { + as: 'user', + foreignKey: { name: 'user_id', allowNull: true } + }) + } + + Schedule.promise_to_build_default_for = function(args) { + const company_id = args.company_id + const user_id = args.user_id + + if (!company_id && !user_id) { + throw new Error('Needs to have either company_id or user_id') + } + + const default_schedule = sequelize.models.Schedule.build({ + company_id, + user_id + }) + + return Promise.resolve(default_schedule) + } + + Schedule.prototype.is_user_specific = function() { + return !!this.user_id + } + + Schedule.prototype.is_it_working_day = function(args) { + const day = args.day + + if (!day) { + throw new Error('"is_it_working_day" requires to have "day" parameter') + } + + return ( + this[ + moment + .utc(day) + .format('dddd') + .toLowerCase() + ] === works_whole_day() + ) + } + + Schedule.prototype.works_monday = function() { + return this.monday === works_whole_day() + } + + Schedule.prototype.works_tuesday = function() { + return this.tuesday === works_whole_day() + } + + Schedule.prototype.works_wednesday = function() { + return this.wednesday === works_whole_day() + } + + Schedule.prototype.works_thursday = function() { + return this.thursday === works_whole_day() + } + + Schedule.prototype.works_friday = function() { + return this.friday === works_whole_day() + } + + Schedule.prototype.works_saturday = function() { + return this.saturday === works_whole_day() + } + + Schedule.prototype.works_sunday = function() { + return this.sunday === works_whole_day() + } + + return Schedule +} diff --git a/lib/model/db/user.js b/lib/model/db/user.js index a93181474..817bc62ff 100644 --- a/lib/model/db/user.js +++ b/lib/model/db/user.js @@ -1,529 +1,695 @@ -"use strict"; - -var - crypto = require('crypto'), - model = require('../db'), - _ = require('underscore'), - moment = require('moment'), - Promise = require("bluebird"), - config = require('../../config'), - - // User mixins - withCompanyAwareness = require('../mixin/user/company_aware'), - withAbsenceAwareness = require('../mixin/user/absence_aware'); - +'use strict' + +const crypto = require('crypto') +const _ = require('underscore') +const moment = require('moment') +const Promise = require('bluebird') +const config = require('../../config') +const UserAllowance = require('../user_allowance') +const htmlToText = require('html-to-text') +// User mixins +const withCompanyAwareness = require('../mixin/user/company_aware') +const withAbsenceAwareness = require('../mixin/user/absence_aware') +const Sequelize = require('sequelize') +const Op = Sequelize.Op +const { sorter } = require('../../util') + +const LeaveCollectionUtil = require('../leave_collection')() module.exports = function(sequelize, DataTypes) { + const instance_methods = get_instance_methods(sequelize) - var instance_methods = get_instance_methods(sequelize); + withCompanyAwareness.call(instance_methods, sequelize) + withAbsenceAwareness.call(instance_methods, sequelize) - withCompanyAwareness.call(instance_methods, sequelize); - withAbsenceAwareness.call(instance_methods, sequelize); + const class_methods = get_class_methods(sequelize) - var class_methods = get_class_methods(sequelize); + withAssociations.call(class_methods, sequelize) + withScopes.call(class_methods, sequelize) - withAssociations.call(class_methods, sequelize); - - var User = sequelize.define("User", { + const User = sequelize.define( + 'User', + { // TODO add validators! - email : { - type : DataTypes.STRING, - allowNull : false - }, - password : { - type : DataTypes.STRING, - allowNull : false - }, - name : { - type : DataTypes.STRING, - allowNull : false - }, - lastname : { - type : DataTypes.STRING, - allowNull : false - }, - activated : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : false, - comment : 'This flag means that user account was activated, e.g. login', - }, - admin : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : false, - comment : 'Indicate if account can edit company wide settings', - }, - auto_approve : { - type : DataTypes.BOOLEAN, - allowNull : false, - defaultValue : false, - comment : 'Indicate if leave request from current employee are auto approveed', - }, - start_date : { - type : DataTypes.DATE, - allowNull : false, - defaultValue : DataTypes.NOW, - comment : 'Date employee start to work for company', - }, - end_date : { - type : DataTypes.DATE, - allowNull : true, - defaultValue : null, - comment : 'Date emplyee stop working for company', - }, - adjustment : { - type : DataTypes.INTEGER, - allowNull : false, - defaultValue : 0, - comment : 'Adjustment to allowance in current year', - }, - }, { - indexes : [ + email: { + type: DataTypes.STRING, + allowNull: false + }, + slack_username: { + type: DataTypes.STRING, + defaultValue: '', // Migration will not run without a default value. I don't understand why. + allowNull: true + }, + password: { + type: DataTypes.STRING, + allowNull: false + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + lastname: { + type: DataTypes.STRING, + allowNull: false + }, + activated: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'This flag means that user account was activated, e.g. login' + }, + admin: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'Indicate if account can edit company wide settings' + }, + manager: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: 'Indicate if semi admin, department manager' + }, + auto_approve: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: + 'Indicate if leave request from current employee are auto approved' + }, + start_date: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + comment: 'Date employee start to work for company', + get: function() { + return moment + .utc(this.getDataValue('start_date')) + .format('YYYY-MM-DD') + } + }, + end_date: { + type: DataTypes.DATE, + allowNull: true, + defaultValue: null, + comment: 'Date emplyee stop working for company', + get: function() { + const endDate = this.getDataValue('end_date') + if (!endDate) { + return endDate + } + + return moment.utc(endDate).format('YYYY-MM-DD') + } + } + }, + { + underscored: true, + indexes: [ { - fields : ['companyId'], + fields: ['company_id'] }, { - fields : ['lastname'], + fields: ['department_id'] }, + { + fields: ['lastname'] + } ], - classMethods: class_methods, - - instanceMethods : instance_methods, - }); - - return User; -}; - - -/* - * Convinience method that returns an object with definition of User's instance methods. - * - * */ -function get_instance_methods(sequelize) { - - return { - - is_my_password : function( password ) { - return sequelize.models.User.hashify_password( password ) === this.password; - }, - - /* - * Activate user only when it is inactive. - * Return promise that gets user's object. - * */ - maybe_activate : function(){ - if ( ! this.activated ) { - this.activated = true; + hooks: { + beforeDestroy(user, options) { + sequelize.models.DepartmentSupervisor.destroy({ + where: { user_id: user.id } + }) + sequelize.models.Leave.destroy({ where: { user_id: user.id } }) + sequelize.models.Schedule.destroy({ where: { user_id: user.id } }) + sequelize.models.Comment.destroy({ where: { by_user_id: user.id } }) + sequelize.models.UserFeed.destroy({ where: { user_id: user.id } }) + sequelize.models.UserAllowanceAdjustment.destroy({ + where: { user_id: user.id } + }) + sequelize.models.EmailAudit.destroy({ where: { user_id: user.id } }) + } } - return this.save(); - }, + } + ) + + Object.assign(User, class_methods) + Object.assign(User.prototype, instance_methods) + + /* + * Convenience method that returns an object with definition of User's instance methods. + * + * */ + function get_instance_methods(sequelize) { + return { + is_my_password: function(password) { + return ( + sequelize.models.User.hashify_password(password) === this.password + ) + }, - is_admin : function() { - return this.admin === true; - }, + /* + * Activate user only when it is inactive. + * Return promise that gets user's object. + */ + maybe_activate: function() { + if (!this.activated) { + this.activated = true + } + return this.save() + }, - /* - * Indicates is leave requests from current user are automatically approved - * */ - is_auto_approve : function(){ - return this.auto_approve === true; - }, + is_admin: function() { + return this.admin === true + }, - full_name : function() { - return this.name + ' ' + this.lastname; - }, + is_manager: function() { + return this.manager === true + }, - promise_users_I_can_manage : function(){ - var this_user = this; + /* + * Indicates is leave requests from current user are automatically approved + */ + is_auto_approve: function() { + return this.auto_approve === true + }, - // Check if current user is admin, then fetch all users form company - if ( this_user.is_admin() ) { + full_name: function() { + return this.name + ' ' + this.lastname + }, - return this_user.get_company_with_all_users() - .then(function(company){ - return Promise.resolve( company.users ); - }); - } + /* + * Indicates if the user is active + */ + is_active: function() { + return this.end_date === null || moment(this.end_date).isAfter(moment()) + }, - // If current user has any departments under supervision then get - // all users from those departments plus user himself, - // if no supervised users an array with only current user is returned - return this_user.getSupervised_departments({ - include : [{ - model : sequelize.models.User, - as : 'users', - }], - }) - .then(function(departments){ - var users = _.flatten( - _.map( - departments, - function(department){ return department.users; } - ) - ); + // TODO VPP: rename this method as its name misleading: it returns all users + // managed by current users + user itself, so it should be something like + // "promise_all_supervised_users_plus_me" + // In fact this method probably have to be ditched in favour of more granular ones + // + promise_users_I_can_manage: async function() { + const self = this + + let users = [] + + if (self.is_admin()) { + // Check if current user is admin, then fetch all users form company + const company = await self.getCompany({ + scope: ['with_all_users'] + }) + + users = company.users + } else { + // If current user has any departments under supervision then get + // all users from those departments plus user himself, + // if no supervised users an array with only current user is returned + const departments = await self.promise_supervised_departments() + + users = departments.map(({ users }) => users).flat() + } // Make sure current user is considered as well - users.push(this_user); + users.push(self) // Remove duplicates - users = _.uniq( - users, - function(user){ return user.id; } - ); + users = _.uniq(users, ({ id }) => id) // Order by last name - users = _.sortBy( - users, - function(user){ return user.lastname; } - ); - - return users; - }); + users = users.sort((a, b) => sorter(a.lastname, b.lastname)) - }, // promise_users_I_can_manage + return users + }, - promise_superviser : function(){ + /* + * Return user's manager, the head of department user belongs to + * + * */ + promise_manager: function() { return this.getDepartment({ - include : [{ - model : sequelize.models.User, - as : 'boss', - }] - }) - .then(function(department){ - return Promise.resolve( department.boss ); - }); - }, - - - calculate_number_of_days_available_in_allowance : function(year){ - if (! year ){ - year = moment().format('YYYY'); - } - return this.calculate_total_number_of_days_n_allowance(year) - - this.calculate_number_of_days_taken_from_allowance({year : year}); - }, + scope: ['with_manager'] + }).then(department => Promise.resolve(department.manager)) + }, + /* + * Return users who could supervise current user, that is those who could + * approve its leave requests and who can create leave requests on behalf of + * those user. + * + * */ + promise_supervisors: function() { + return this.getDepartment({ + scope: ['with_manager', 'with_supervisors'] + }).then(department => + Promise.resolve( + _.flatten([department.manager, department.supervisors]) + ) + ) + }, - reload_with_leave_details : function(args){ - var year = args.year || moment(), - self = this; - - return Promise.join( - self.promise_my_active_leaves(args), - self.getDepartment(), - self.promise_schedule_I_obey(), - function(leaves, department, schedule){ - self.my_leaves = leaves; - self.department = department; - - // Note: we do not do anithing with scheduler as "promise_schedule_I_obey" - // sets the "cached_schedule" attribute under the hood, which is used in - // synchronous code afterwards. Yes... it is silly, but it is where we are - // at thi moment after mixing non blocking and blocking code together... - // - return Promise.resolve(self); - } - ); + promise_supervised_departments: function() { + const self = this - }, + return ( + sequelize.models.DepartmentSupervisor.findAll({ + where: { user_id: self.id } + }) + // Obtain departments current user supervises as secondary supervisor + .then(department_supervisors => + department_supervisors.map(obj => obj.department_id) + ) + .then(department_ids => { + if (!department_ids) { + department_ids = [] + } + + return sequelize.models.Department.scope( + 'with_simple_users' + ).findAll({ + where: { + [Op.or]: [ + { id: { [Op.in]: department_ids } }, + { manager_id: self.id } + ] + } + }) + }) + .catch(error => { + console.error( + 'Unable to retrieve DepartmentSupervisor for user_id=' + + self.id + + ' : ' + + error, + error.stack + ) + return error + }) + ) + }, - // This method reload user object to have all necessary information to render - // each page - reload_with_session_details : function(){ - var self = this; - return Promise.join( - self.promise_users_I_can_manage(), - self.get_company_with_all_leave_types(), - self.promise_schedule_I_obey(), - function(users, company, schedule){ - self.supervised_users = users || []; - self.company = company; - - // Note: we do not do anithing with scheduler as "promise_schedule_I_obey" - // sets the "cached_schedule" attribute under the hood, which is used in - // synchronous code afterwards. Yes... it is silly, but it is where we are - // at thi moment after mixing non blocking and blocking code together... - - return Promise.resolve(self); - }); - }, + promise_supervised_users: function() { + const self = this + return self.promise_supervised_departments().then(departments => + self.sequelize.models.User.findAll({ + where: { department_id: departments.map(d => d.id) } + }) + ) + }, - remove : function() { - var self = this; + // Generate object that represent Employee allowance + promise_allowance: function(args) { + args = args || {} + // Override user to be current one + args.user = this + return UserAllowance.promise_allowance(args) + }, - // make sure I am not admin, otherwise throw an error - if (self.is_admin()) { - throw new Error('Cannot remove administrator user'); - } + reload_with_leave_details: function(args) { + const self = this + const dbModel = self.sequelize.models + + return Promise.all([ + self + .promise_my_active_leaves(args) + .then(leaves => + LeaveCollectionUtil.enrichLeavesWithComments({ leaves, dbModel }) + ), + self.getDepartment(), + self.promise_schedule_I_obey() + ]) + .then(([leaves, department, schedule]) => { + self.my_leaves = leaves + self.department = department + // Note: we do not do anything with scheduler as "promise_schedule_I_obey" + // sets the "cached_schedule" attribute under the hood, which is used in + // synchronous code afterwards. Yes... itaza`z is silly, but it is where we are + // at thi moment after mixing non blocking and blocking code together... + // + return self + }) + .catch(error => { + console.error( + 'Unable to load leaves details for user ' + + self.email + + ' : ' + + error, + error.stack + ) + return Promise.reject(error) + }) + }, - // make sure I am not supervisor, otherwise throw an error - return self.getSupervised_departments() - .then(function(departments){ - if (departments.length > 0){ - throw new Error("Cannot remove supervisor"); + // This method reload user object to have all necessary information to render + // each page + reload_with_session_details: function() { + const self = this + + return Promise.join( + self.promise_users_I_can_manage(), + self.get_company_with_all_leave_types(), + self.promise_schedule_I_obey(), + (users, company, schedule) => { + self.supervised_users = users || [] + self.company = company + + // Note: we do not do anything with scheduler as "promise_schedule_I_obey" + // sets the "cached_schedule" attribute under the hood, which is used in + // synchronous code afterwards. Yes... it is silly, but it is where we are + // at thi moment after mixing non blocking and blocking code together... + + return Promise.resolve(self) } - - return self.getMy_leaves() - }) - .then(function(leaves){ - // remove all leaves - return Promise.all( - _.map( leaves, function(leave){ return leave.destroy(); }) - ); + ).catch(error => { + console.error( + 'Unable to load session details for user ' + + self.email + + ' : ' + + error, + error.stack + ) + return Promise.reject(error) }) + }, - // remove user record - .then(function(){ - return self.destroy(); - }) + remove: function() { + const self = this - }, + // make sure I am not admin, otherwise throw an error + if (self.is_admin()) { + throw new Error('Cannot remove administrator user') + } - get_reset_password_token : function(){ - var self = this; + // make sure I am not supervisor, otherwise throw an error + return ( + self + .promise_supervised_departments() + .then(departments => { + if (departments.length > 0) { + throw new Error('Cannot remove supervisor') + } + + return self.getMy_leaves() + }) + .then(leaves => + // remove all leaves + Promise.all(_.map(leaves, leave => leave.destroy())) + ) - return new Buffer( self.email + ' ' + self.Model.hashify_password( self.password ) ).toString('base64'); - }, + // remove user record + .then(() => self.destroy()) + ) + }, - // Accept an object that represent email to be sent to current user and - // record it into the corresponding audit table - // - record_email_addressed_to_me : function(email_obj) { - - // validate email object to contain all necessary fields - if ( ! email_obj || - ! email_obj.hasOwnProperty('subject') || - ! email_obj.subject || - ! email_obj.hasOwnProperty('body') || - ! email_obj.body - ) { - throw new Error( - 'Got incorrect parameters. There should be an object '+ - 'to represent and email and contain subject and body' - ); - } + get_reset_password_token: function() { + const self = this - var promise_action = this.sequelize.models.EmailAudit.create({ - email : this.email, - subject : email_obj.subject, - body : email_obj.body, - user_id : this.id, - company_id : this.companyId, - }); + return Buffer.from( + self.email + + ' ' + + this.sequelize.models.User.hashify_password(self.password) + ).toString('base64') + }, - promise_action.then(function(audit_record){ - return Promise.resolve(audit_record); - }); + // Accept an object that represent email to be sent to current user and + // record it into the corresponding audit table + // + record_email_addressed_to_me: function(email_obj) { + // validate email object to contain all necessary fields + if ( + !email_obj || + !email_obj.hasOwnProperty('subject') || + !email_obj.subject || + !email_obj.hasOwnProperty('body') || + !email_obj.body + ) { + throw new Error( + 'Got incorrect parameters. There should be an object ' + + 'to represent and email and contain subject and body' + ) + } - return promise_action; - }, + const promise_action = this.sequelize.models.EmailAudit.create({ + email: this.email, + subject: htmlToText.fromString(email_obj.subject), + body: htmlToText.fromString(email_obj.body), + user_id: this.id, + company_id: this.company_id + }) - promise_schedule_I_obey : function(){ - var self = this; + return promise_action + }, - if ( self.cached_schedule ) { - return Promise.resolve( self.cached_schedule ); - } + promise_schedule_I_obey: function() { + const self = this - return self.sequelize.models.Schedule - .findAll({ - where : { - $or : [ - { user_id : self.id }, - { company_id : self.companyId }, - ] - } - }) - .then(function(schedules){ + if (self.cached_schedule) { + return Promise.resolve(self.cached_schedule) + } + return self.sequelize.models.Schedule.findAll({ + where: { + [Op.or]: [{ user_id: self.id }, { company_id: self.company_id }] + } + }).then(schedules => { // no schedules for current user in DB, return default one if (schedules.length === 0) { - return self.sequelize.models.Schedule - .promise_to_build_default_for({ company_id : self.companyId }) - .then(function(sch){ self.cached_schedule = sch; return Promise.resolve(sch) }); + return self.sequelize.models.Schedule.promise_to_build_default_for({ + company_id: self.company_id + }).then(sch => { + self.cached_schedule = sch + return Promise.resolve(sch) + }) } // there are two schedules, presumably one company wide and another // is user specific, return later one if (schedules.length === 2) { return Promise.resolve( - _.find(schedules, function(sch){ return sch.is_user_specific() }) - ) - .then(function(sch){ self.cached_schedule = sch; return Promise.resolve(sch) }); + _.find(schedules, sch => sch.is_user_specific()) + ).then(sch => { + self.cached_schedule = sch + return Promise.resolve(sch) + }) } // single schedule means it is company wide one - return Promise.resolve( schedules.pop() ) - .then(function(sch){ self.cached_schedule = sch; return Promise.resolve(sch) }); - }); - }, - - }; - -}; - -function get_class_methods(sequelize) { - return { - - /* hashify_password( password_string ) : string - * - * For provided string return hashed string. - * - * */ - hashify_password : function( password ) { - return crypto - .createHash('md5') - .update(password + config.get('crypto_secret')) - .digest('hex'); - }, - - - get_user_by_reset_password_token : function(token) { - var self = this, - unpacked_token = new Buffer(token, 'base64').toString('ascii'), - email_and_hashed_password = unpacked_token.split(/\s/); - - return self.find_by_email(email_and_hashed_password[0]) - .then(function(user){ - if (user && self.hashify_password(user.password) === email_and_hashed_password[1]) { - return Promise.resolve(user); - } else { - return Promise.resolve(); - } + return Promise.resolve(schedules.pop()).then(sch => { + self.cached_schedule = sch + return Promise.resolve(sch) + }) }) - }, - - // Get active user by provided email address - find_by_email : function( email ) { - - // TODO validate email - - var condition = { email : email }; - var active_users_filter = this.get_active_user_filter(); - for (var attrname in active_users_filter) { - condition[attrname] = active_users_filter[attrname]; } + } + } + + function get_class_methods(sequelize) { + return { + /* hashify_password( password_string ) : string + * + * For provided string return hashed string. + * + * */ + hashify_password: function(password) { + return crypto + .createHash('md5') + .update( + password + config.get('crypto_secret'), + config.get('crypto_hash_encoding') || 'binary' + ) + .digest('hex') + }, - return this.find({ where : condition }); - }, - - find_by_id : function(id) { - return this.find({ where : {id : id}}); - }, - - /* - * Create new admin user within new environment - company etc - * */ - register_new_admin_user : function(attributes){ - - // TODO add parameters validation - - // Make sure we hash the password before storing it to DB - attributes.password = this.hashify_password(attributes.password); - - var new_departments, - new_user, - country_code = attributes.country_code, - company_name = attributes.company_name; - - delete attributes.company_name; - delete attributes.country_code; - - return sequelize.models.User.find_by_email( attributes.email ) - .then(function(existing_user){ - if (existing_user) { - var error = new Error('Email is already used') - error.show_to_user = true; - throw error; + get_user_by_reset_password_token: function(token) { + const self = this + const unpacked_token = Buffer.from(token, 'base64').toString('ascii') + const email_and_hashed_password = unpacked_token.split(/\s/) + + return self.find_by_email(email_and_hashed_password[0]).then(user => { + if ( + user && + self.hashify_password(user.password) === + email_and_hashed_password[1] + ) { + return Promise.resolve(user) + } else { + return Promise.resolve() } - - return sequelize.models.Company - .create_default_company({ - name : company_name, - country_code : country_code, - }); }) + }, - // Make sure new user is going to be linked with a company - .then(function(company){ - - attributes.companyId = company.id; - attributes.admin = true; - - return company.getDepartments(); - }) - - // Make sure new user is linked with department - .then(function(departments){ - - new_departments = departments; + // Get active user by provided email address + find_by_email: function(email) { + // TODO validate email - attributes.DepartmentId = departments[0].id; + const condition = { email } + const active_users_filter = this.get_active_user_filter() + for (const attrname in active_users_filter) { + condition[attrname] = active_users_filter[attrname] + } - return sequelize.models.User.create( attributes ); - }) + return this.findOne({ where: condition }) + }, - // Make sure new departments know who is their boss - .then(function(user){ - new_user = user; + find_by_id: function(id) { + return this.findOne({ where: { id } }) + }, - return Promise.all(_.map(new_departments, function(department){ - department.bossId = user.id; - return department.save(); - })); - }) + /* + * Create new admin user within new environment - company etc + * */ + register_new_admin_user: function(attributes) { + // TODO add parameters validation + + // Make sure we hash the password before storing it to DB + attributes.password = this.hashify_password(attributes.password) + + let new_departments + let new_user + const country_code = attributes.country_code + const timezone = attributes.timezone + const company_name = attributes.company_name + + delete attributes.company_name + delete attributes.country_code + + return ( + sequelize.models.User.find_by_email(attributes.email) + .then(existing_user => { + if (existing_user) { + const error = new Error('Email is already used') + error.show_to_user = true + throw error + } + + console.log('attribute.name is', attributes.name) + if (attributes.name.toLowerCase().indexOf('http') >= 0) { + const error = new Error('Name cannot have links') + error.show_to_user = true + throw error + } + + return sequelize.models.Company.create_default_company({ + name: company_name, + country_code, + timezone + }) + }) + + // Make sure new user is going to be linked with a company + .then(company => { + attributes.company_id = company.id + attributes.admin = true + + return company.getDepartments() + }) + + // Make sure new user is linked with department + .then(departments => { + new_departments = departments + + attributes.department_id = departments[0].id + + return sequelize.models.User.create(attributes) + }) + + // Make sure new departments know who is their manager + .then(user => { + new_user = user + + return Promise.all( + _.map(new_departments, department => { + department.manager_id = user.id + return department.save() + }) + ) + }) + + // Return promise with newly created user + .then(() => Promise.resolve(new_user)) + ) + }, - // Return promise with newly created user - .then(function(){ - return Promise.resolve(new_user); - }); - }, + get_active_user_filter: function() { + return { + [Op.or]: [ + { end_date: { [Op.eq]: null } }, + { + end_date: { + [Op.gte]: moment + .utc() + .startOf('day') + .format('YYYY-MM-DD') + } + } + ] + } + } + } + } // END of class methods + + // Mixin-like function that injects definition of User's associations into supplied object. + // (Define relations between User class and other entities in the domain). + // + function withAssociations() { + this.associate = function(models) { + models.User.belongsTo(models.Company, { + as: 'company', + foreignKey: { name: 'company_id', allowNull: false } + }) + models.User.belongsTo(models.Department, { + as: 'department', + foreignKey: { name: 'department_id', allowNull: false } + }) + models.User.hasMany(models.Leave, { + as: 'my_leaves', + foreignKey: { name: 'user_id', allowNull: false } + }) + models.User.hasMany(models.UserFeed, { + as: 'feeds', + foreignKey: { name: 'user_id', allowNull: false } + }) + models.User.hasMany(models.UserAllowanceAdjustment, { + as: 'adjustments', + foreignKey: { name: 'user_id', allowNull: false } + }) + } + } + + function withScopes() { + this.loadScope = function(models) { + models.User.addScope('active', () => ({ + where: models.User.get_active_user_filter() + })) + + models.User.addScope('withDepartments', { + include: [ + { + model: models.Department, + as: 'department' + } + ] + }) - get_active_user_filter : function(){ - return { - $or : [ - { end_date : {$eq : null}}, - { end_date : {$gte : moment().startOf('day').format('YYYY-MM-DD') }}, - ], - }; - }, + models.User.addScope('with_simple_leaves', () => ({ + include: [ + { + model: models.Leave, + as: 'my_leaves', + where: { + [Op.and]: [ + { status: { [Op.ne]: models.Leave.status_rejected() } }, + { status: { [Op.ne]: models.Leave.status_canceled() } } + ] + } + } + ] + })) + } + } - }; -}; // END of class methods - - -// Mixin-like function that injects definition of User's associations into supplied object. -// (Define relations between User class and other entities in the domain). -// -function withAssociations() { - - this.associate = function(models){ - - models.User.belongsTo(models.Company, { - as : 'company', - }); - models.User.belongsTo(models.Department, { - as : 'department', - foreignKey : 'DepartmentId', - }); - models.User.hasMany(models.Leave, { - as : 'my_leaves', - foreignKey : 'userId', - }); - models.User.hasMany(models.Leave, { - as : 'supervised_leaves', - foreignKey : 'approverId', - }); - models.User.hasMany(models.Department, { - as : 'supervised_departments', - foreignKey : 'bossId', - constraints: false, - }); - models.User.hasMany(models.UserFeed, { - as : 'feeds', - foreignKey : 'userId', - }); - }; + return User } diff --git a/lib/model/db/user_allowance_adjustment.js b/lib/model/db/user_allowance_adjustment.js new file mode 100644 index 000000000..a2cc311ad --- /dev/null +++ b/lib/model/db/user_allowance_adjustment.js @@ -0,0 +1,53 @@ +'use strict' + +const moment = require('moment') + +module.exports = function(sequelize, DataTypes) { + const UserAllowanceAdjustment = sequelize.define( + 'UserAllowanceAdjustment', + { + year: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: moment.utc().format('YYYY'), + comment: 'Year when adjustment is applied' + }, + adjustment: { + type: DataTypes.FLOAT, + allowNull: false, + defaultValue: 0, + comment: 'Adjustment to allowance in current year' + }, + carried_over_allowance: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + comment: + 'Additional allowance to use based on un-used holidays in previous year' + } + }, + { + underscored: true, + freezeTableName: true, + timestamps: true, + createdAt: 'created_at', + updatedAt: false, + tableName: 'user_allowance_adjustment', + indexes: [ + { + fields: ['user_id', 'year'], + unique: true + } + ] + } + ) + + UserAllowanceAdjustment.associate = function(models) { + UserAllowanceAdjustment.belongsTo(models.User, { + as: 'user', + foreignKey: { name: 'user_id', allowNull: false } + }) + } + + return UserAllowanceAdjustment +} diff --git a/lib/model/db/user_feed.js b/lib/model/db/user_feed.js index 3f7f45c45..027b63645 100644 --- a/lib/model/db/user_feed.js +++ b/lib/model/db/user_feed.js @@ -1,66 +1,66 @@ -"use strict"; +'use strict' -var - uuid = require('node-uuid'); +const uuid = require('node-uuid') module.exports = function(sequelize, DataTypes) { - var UserFeed = sequelize.define("UserFeed", { - name : { - type : DataTypes.STRING, - allowNull : false, - }, - feed_token : { - type : DataTypes.STRING, - allowNull : false, - }, - type : { - // NOTE: 'wallchart' and 'teamview' are essentially the same thing - // later one used to be know as former, from now on use 'teamview' - // and keep old what for data compatibility - type : DataTypes.ENUM('calendar', 'wallchart', 'teamview', 'company'), - allowNull : false, + const UserFeed = sequelize.define( + 'UserFeed', + { + name: { + type: DataTypes.STRING, + allowNull: false + }, + feed_token: { + type: DataTypes.STRING, + allowNull: false + }, + type: { + // NOTE: 'wallchart' and 'teamview' are essentially the same thing + // later one used to be know as former, from now on use 'teamview' + // and keep old what for data compatibility + type: DataTypes.ENUM('calendar', 'wallchart', 'teamview', 'company'), + allowNull: false + } }, - }, { + { + underscored: true + } + ) - classMethods: { - associate : function( models ) { - UserFeed.belongsTo(models.User, {as : 'user'}); - }, + UserFeed.associate = function(models) { + UserFeed.belongsTo(models.User, { + as: 'user', + foreignKey: { name: 'user_id', allowNull: false } + }) + } - promise_new_feed : function(args){ - var self = this, - user = args.user, - type = args.type; + UserFeed.promise_new_feed = function(args) { + const self = this + const user = args.user + const type = args.type - return self - .find({ where : {userId : user.id, type : type} }) - .then(function(feed){ - if ( feed ) { - feed.feed_token = uuid.v4(); - return feed.save(); - } else { - return self.create({ - name : "Calendar Feed", - feed_token : uuid.v4(), - type : type, - userId : user.id, - }); - } - }) - }, + return self.findOne({ where: { user_id: user.id, type } }).then(feed => { + if (feed) { + feed.feed_token = uuid.v4() + return feed.save() + } else { + return self.create({ + name: 'Calendar Feed', + feed_token: uuid.v4(), + type, + user_id: user.id + }) + } + }) + } - }, - - instanceMethods : { - is_calendar : function() { - return this.type === 'calendar'; - }, + UserFeed.prototype.is_calendar = function() { + return this.type === 'calendar' + } - is_team_view : function(){ - return this.type === 'wallchart' || this.type === 'teamview'; - }, - }, - }); + UserFeed.prototype.is_team_view = function() { + return this.type === 'wallchart' || this.type === 'teamview' + } - return UserFeed; -}; + return UserFeed +} diff --git a/lib/model/leave/index.js b/lib/model/leave/index.js new file mode 100644 index 000000000..e3ad149f5 --- /dev/null +++ b/lib/model/leave/index.js @@ -0,0 +1,155 @@ +'use strict' + +const Promise = require('bluebird') +const Joi = require('joi') +const moment = require('moment') +const Exception = require('../../error') +const { commentLeave } = require('../comment') +const Models = require('../db') + +const schemaCreateNewLeave = Joi.object() + .required() + .keys({ + for_employee: Joi.object().required(), + of_type: Joi.object().required(), + with_parameters: Joi.object().required() + }) + +/* + * Create new leave for provided parameters. + * Returns promise that is resolved with newly created leave row + * */ +function createNewLeave(args) { + args = Joi.attempt(args, schemaCreateNewLeave, 'Failed to validate arguments') + + const employee = args.for_employee + const leave_type = args.of_type + const valide_attributes = args.with_parameters + + const start_date = moment.utc(valide_attributes.from_date) + const end_date = moment.utc(valide_attributes.to_date) + + // log what dates are being used + console.log( + 'file: leave/model/leave/index.js start_date', + start_date.format('YYYY-MM-DD'), + end_date.format('YYYY-MM-DD') + ) + + // Check that start date is not bigger then end one + if (start_date.toDate() > end_date.toDate()) { + Exception.throwUserError({ + user_error: 'Start date is later than end date', + system_error: `Failed to add new Leave for user ${ + employee.id + } ``because start date ${start_date} happnned to be after end date ${end_date}` + }) + } + + const comment = valide_attributes.reason + const company_id = employee.company_id + + // Make sure that booking to be created is not going to ovelap with + // any existing bookings + return Promise.try(() => employee.validate_overlapping(valide_attributes)) + .then(() => employee.promise_manager()) + .then(main_supervisor => { + const new_leave_status = Models.Leave.does_skip_approval( + employee, + leave_type + ) + ? Models.Leave.status_approved() + : Models.Leave.status_new() + + // Following statement creates in memory only leave object + // it is not in database until .save() method is called + return Promise.resolve( + Models.Leave.build({ + user_id: employee.id, + leave_type_id: leave_type.id, + status: new_leave_status, + approver_id: main_supervisor.id, + employee_comment: valide_attributes.reason, + + date_start: start_date.format('YYYY-MM-DD'), + date_end: end_date.format('YYYY-MM-DD'), + day_part_start: valide_attributes.from_date_part, + day_part_end: valide_attributes.to_date_part + }) + ) + }) + + .then(leave_to_create => + employee + .validate_leave_fits_into_remaining_allowance({ + year: start_date, + leave_type, + leave: leave_to_create + }) + .then(() => leave_to_create.save()) + ) + .then(leave => + commentLeaveIfNeeded({ leave, comment, company_id }).then(() => leave) + ) + .then(leave => Promise.resolve(leave)) +} + +const commentLeaveIfNeeded = ({ leave, comment, company_id }) => + comment ? commentLeave({ leave, comment, company_id }) : Promise.resolve() + +const getLeaveForUserView = async ({ actingUser, leaveId, dbModel }) => { + const [leave] = await dbModel.Leave.findAll({ + where: { + id: leaveId + }, + include: [ + { + model: dbModel.User, + as: 'user', + where: { + company_id: actingUser.company_id + } + } + ] + }) + + if (!leave) { + throw new Error( + `User [${ + actingUser.id + }] tried to access leave [${leaveId}] which does not belong to the same company.` + ) + } + + return leave +} + +const doesUserHasExtendedViewOfLeave = async ({ user, leave }) => { + if (user.company_id !== (await leave.getUser()).company_id) { + throw new Error( + `User [${user.id}] and leave [${leave.id}] do not share company.` + ) + } + + let extendedView = false + + if (user.is_admin()) { + extendedView = true + } + + if (!extendedView) { + const reports = await user.promise_supervised_users() + + if (reports.filter(u => `${u.id}` === `${leave.user_id}`).length > 0) { + extendedView = true + } + } + + return extendedView +} + +module.exports = { + createNewLeave, + doesUserHasExtendedViewOfLeave, + getLeaveForUserView +} diff --git a/lib/model/leave_collection.js b/lib/model/leave_collection.js new file mode 100644 index 000000000..717959363 --- /dev/null +++ b/lib/model/leave_collection.js @@ -0,0 +1,86 @@ +'use strict' + +const Promise = require('bluebird') +const moment = require('moment') +const _ = require('underscore') +const { Op } = require('sequelize') + +function promise_to_group_leaves(leaves) { + if (!leaves) { + throw new Error('Did not get "leaves" in promise_to_group_leaves') + } + + let grouped_leaves = {} + + // Group leaves by years + leaves.forEach(leave => { + const year = moment.utc(leave.get_start_leave_day().date).format('YYYY') + + if (!grouped_leaves[year]) { + grouped_leaves[year] = { + year, + leaves: [] + } + } + + grouped_leaves[year].leaves.push(leave) + }) + + // Sort year groups + grouped_leaves = _.values(grouped_leaves).sort((a, b) => + a.year > b.year ? -1 : a.year < b.year ? 1 : 0 + ) + + // Calculate total allowance deduction per group + grouped_leaves.forEach(group => { + group.total_deduction = _.reduce( + group.leaves.map(leave => leave.get_deducted_days_number()), + (memo, number) => memo + number, + 0 + ) + }) + + return Promise.resolve(grouped_leaves) +} + +/* + * Simple function that sorts array of Leave objects in default way. + * + * */ + +function promise_to_sort_leaves(leaves) { + return Promise.resolve( + leaves.sort((a, b) => + a.date_start > b.date_start ? -1 : a.date_start < b.date_start ? 1 : 0 + ) + ) +} + +const enrichLeavesWithComments = async ({ leaves, dbModel }) => { + const comments = await dbModel.Comment.findAll({ + where: { + entity_id: { [Op.in]: leaves.map(l => l.id) }, + entity_type: dbModel.Comment.getEntityTypeLeave() + } + }) + + // Map comments by Leave ID + const commentMap = comments.reduce((acc, c) => { + acc[c.entity_id] = c + return acc + }, {}) + + leaves + .filter(l => commentMap[l.id] !== undefined) + .forEach(l => (l.comment = commentMap[l.id])) + + return leaves +} + +module.exports = function() { + return { + enrichLeavesWithComments, + promise_to_group_leaves, + promise_to_sort_leaves + } +} diff --git a/lib/model/leave_day.js b/lib/model/leave_day.js index 393822894..b94cf48c3 100644 --- a/lib/model/leave_day.js +++ b/lib/model/leave_day.js @@ -9,52 +9,63 @@ * * */ -"use strict"; +'use strict' -var - _ = require('underscore'), - moment = require('moment'); +const _ = require('underscore') +const moment = require('moment') function LeaveDay(args) { - // Make sure all required data is provided - _.each( - ['date', 'day_part', 'sequelize'], - function(property){ - if (! _.has(args, property)) { - throw new Error('No mandatory '+property+' was provided'); - } + _.each(['date', 'day_part', 'sequelize', 'leave_type_id'], property => { + if (!_.has(args, property)) { + throw new Error('No mandatory ' + property + ' was provided') } - ); + }) - this.date = args.date; - this.day_part = args.day_part; - this.sequelize = args.sequelize; + this.date = args.date + this.day_part = args.day_part + this.sequelize = args.sequelize + this.morning_leave_type_id = this.afternoon_leave_type_id = args.leave_type_id +} -}; +LeaveDay.prototype.is_all_day_leave = function() { + return ( + String(this.day_part) === + String(this.sequelize.models.Leave.leave_day_part_all()) + ) +} +LeaveDay.prototype.is_morning_leave = function() { + return ( + String(this.day_part) === + String(this.sequelize.models.Leave.leave_day_part_morning()) + ) +} -LeaveDay.prototype.is_all_day_leave = function(){ - return String(this.day_part) === String(this.sequelize.models.Leave.leave_day_part_all()); -}; +LeaveDay.prototype.is_afternoon_leave = function() { + return ( + String(this.day_part) === + String(this.sequelize.models.Leave.leave_day_part_afternoon()) + ) +} -LeaveDay.prototype.is_morning_leave = function(){ - return String(this.day_part) === String(this.sequelize.models.Leave.leave_day_part_morning()); -}; +LeaveDay.prototype.get_morning_leave_type_id = function() { + return String(this.morning_leave_type_id) +} -LeaveDay.prototype.is_afternoon_leave = function(){ - return String(this.day_part) === String(this.sequelize.models.Leave.leave_day_part_afternoon()); -}; +LeaveDay.prototype.get_afternoon_leave_type_id = function() { + return String(this.afternoon_leave_type_id) +} // Set current object to be as one for All day leave, but the state is not // saved into database. It is used when showing calendar to user and // there are two half days that fit into one whole day LeaveDay.prototype.pretend_to_be_full_day = function() { - return this.day_part = this.sequelize.models.Leave.leave_day_part_all(); -}; + return (this.day_part = this.sequelize.models.Leave.leave_day_part_all()) +} -LeaveDay.prototype.get_pretty_date = function(){ - return moment(this.date).format('YYYY-MM-DD'); -}; +LeaveDay.prototype.get_pretty_date = function() { + return moment.utc(this.date).format('YYYY-MM-DD') +} -module.exports = LeaveDay; +module.exports = LeaveDay diff --git a/lib/model/leave_request_parameters.js b/lib/model/leave_request_parameters.js index dfa0d93c9..5254b41f0 100644 --- a/lib/model/leave_request_parameters.js +++ b/lib/model/leave_request_parameters.js @@ -1,105 +1,120 @@ +'use strict' -'use strict'; - -var - _ = require('underscore'), - model = require('../model/db'), - moment = require('moment'); +const _ = require('underscore') +const model = require('../model/db') +const moment = require('moment') function LeaveRequest(args) { - var me = this; - - // Make sure all required data is provided - _.each( - [ - 'leave_type','from_date','from_date_part', - 'to_date', 'to_date_part', 'reason' - ], - function(property){ - if (! _.has(args, property)) { - throw new Error('No mandatory '+property+' was provided to LeaveRequest constructor'); - } - } - ); - - // From date should not be bigger then to - if (moment(args.from_date).toDate() > moment(args.to_date).toDate()){ - throw new Error( 'From date should be before To date at LeaveRequest constructor' ); + const me = this + + // Make sure all required data is provided + _.each( + [ + 'leave_type', + 'from_date', + 'from_date_part', + 'to_date', + 'to_date_part', + 'reason' + ], + property => { + if (!_.has(args, property)) { + throw new Error( + 'No mandatory ' + + property + + ' was provided to LeaveRequest constructor' + ) + } } - - _.each( - [ - 'leave_type','from_date','from_date_part', - 'to_date', 'to_date_part', 'reason', 'user' - ], - function(property){ me[property] = args[property]; } - ); + ) + + // From date should not be bigger then to + if (moment.utc(args.from_date).toDate() > moment.utc(args.to_date).toDate()) { + throw new Error( + 'From date should be before To date at LeaveRequest constructor' + ) + } + + _.each( + [ + 'leave_type', + 'from_date', + 'from_date_part', + 'to_date', + 'to_date_part', + 'reason', + 'user' + ], + property => { + me[property] = args[property] + } + ) } -LeaveRequest.prototype.as_data_object = function(){ - var obj = {}, - me = this; - - _.each( - [ - 'leave_type','from_date','from_date_part', - 'to_date', 'to_date_part', 'reason', 'user' - ], - function(property){ obj[property] = me[property]; } - ); - - return obj; -}; - -LeaveRequest.prototype.is_within_one_day = function(){ - return moment(this.from_date).format('YYYY-MM-DD') - === - moment(this.to_date).format('YYYY-MM-DD'); -}; - -LeaveRequest.prototype._does_fit_with_point = function(leave_day, point_name){ - var return_val = false; - - if ( - ( - moment(leave_day.date).format('YYYY-MM-DD') - === - moment(this[point_name]).format('YYYY-MM-DD') - ) - && - (! leave_day.is_all_day_leave()) - && - (String(this[point_name+'_part']) !== String(model.Leave.leave_day_part_all())) - && - (String(leave_day.day_part) !== String(this[point_name+'_part'])) - ) { - return_val = true; +LeaveRequest.prototype.as_data_object = function() { + const obj = {} + const me = this + + _.each( + [ + 'leave_type', + 'from_date', + 'from_date_part', + 'to_date', + 'to_date_part', + 'reason', + 'user' + ], + property => { + obj[property] = me[property] } + ) - return return_val; -}; + return obj +} + +LeaveRequest.prototype.is_within_one_day = function() { + return ( + moment.utc(this.from_date).format('YYYY-MM-DD') === + moment.utc(this.to_date).format('YYYY-MM-DD') + ) +} + +LeaveRequest.prototype._does_fit_with_point = function(leave_day, point_name) { + let return_val = false + + if ( + moment.utc(leave_day.date).format('YYYY-MM-DD') === + moment.utc(this[point_name]).format('YYYY-MM-DD') && + !leave_day.is_all_day_leave() && + String(this[point_name + '_part']) !== + String(model.Leave.leave_day_part_all()) && + String(leave_day.day_part) !== String(this[point_name + '_part']) + ) { + return_val = true + } + + return return_val +} // Check if start date or end date of current object fits with provided leave_day // instance. // By fitting I mean days are the same and both of them are // halfs of different types. // -LeaveRequest.prototype.does_fit_with_leave_day = function(leave_day){ - - return this._does_fit_with_point(leave_day, 'from_date') - || - this._does_fit_with_point(leave_day, 'to_date'); -}; - -LeaveRequest.prototype.does_fit_with_leave_day_at_start = function(leave_day){ - - return this._does_fit_with_point(leave_day, 'from_date'); -}; - -LeaveRequest.prototype.does_fit_with_leave_day_at_end = function(leave_day){ +LeaveRequest.prototype.does_fit_with_leave_day = function(leave_day) { + return ( + this._does_fit_with_point(leave_day, 'from_date') || + this._does_fit_with_point(leave_day, 'to_date') + ) +} - return this._does_fit_with_point(leave_day, 'to_date'); -}; +LeaveRequest.prototype.does_fit_with_leave_day_at_start = function(leave_day) { + return this._does_fit_with_point(leave_day, 'from_date') +} +LeaveRequest.prototype.does_fit_with_leave_day_at_end = function(leave_day) { + return this._does_fit_with_point(leave_day, 'to_date') +} -module.exports = LeaveRequest; +module.exports = LeaveRequest diff --git a/lib/model/mixin/user/absence_aware.js b/lib/model/mixin/user/absence_aware.js index b8add69c5..76b5155a7 100644 --- a/lib/model/mixin/user/absence_aware.js +++ b/lib/model/mixin/user/absence_aware.js @@ -1,498 +1,733 @@ - /* * Mixin that inject to user model object consumer set of methods necessary for - * dealing with abcenses. + * dealing with absences. * * */ -'use strict'; +'use strict' + +const { sorter } = require('../../../util') -var - _ = require('underscore'), - Promise = require("bluebird"), - CalendarMonth = require('../../calendar_month'), - moment = require('moment'); +const _ = require('underscore') +const Promise = require('bluebird') +const CalendarMonth = require('../../calendar_month') +const LeaveCollectionUtil = require('../../leave_collection')() +const moment = require('moment') -module.exports = function(sequelize){ +const Sequelize = require('sequelize') +const Op = Sequelize.Op - this._get_calendar_months_to_show = function(args){ - var year = args.year, - show_full_year = args.show_full_year; +module.exports = function (sequelize) { + this._get_calendar_months_to_show = function (args) { + const self = this + const year = args.year + const show_full_year = args.show_full_year if (show_full_year) { - return _.map([1,2,3,4,5,6,7,8,9,10,11,12], function(i){ - return moment(year.format('YYYY')+'-'+i+'-01'); - }); + return _.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], i => + moment.utc(year.format('YYYY') + '-' + i + '-01') + ) } - return _.map([0,1,2,3], function(delta){ - return moment().add(delta, 'months').startOf('month'); + return _.map([0, 1, 2, 3], delta => + self.company + .get_today() + .add(delta, 'months') + .startOf('month') + ) + } + + this.promise_calendar = function (args) { + const this_user = this + const year = args.year || this_user.company.get_today() + const show_full_year = args.show_full_year || false + const model = sequelize.models + // Find out if we need to show multi year calendar + const is_multi_year = this_user.company.get_today().month() > 8 + + const months_to_show = this_user._get_calendar_months_to_show({ + year: year.clone(), + show_full_year }) - }; - - - this.promise_calendar = function(args) { - var year = args.year || moment(), - show_full_year = args.show_full_year || false, - model = sequelize.models, - this_user = this, - // Find or if we need to show multi year calendar - is_multi_year = moment().month() > 8; - - var months_to_show = this_user._get_calendar_months_to_show({ - year : year.clone(), - show_full_year : show_full_year - }); + console.log('promise_calendar months_to_show:', months_to_show) return Promise.join( + Promise.try(() => this_user.getDepartment()), - Promise.try(function(){ - return this_user.getDepartment(); - }), + Promise.try(() => + this_user.getCompany({ + scope: ['with_bank_holidays', 'with_leave_types'] + }) + ), - Promise.try(function(){ - return this_user.getCompany({ - include:[ - { model : model.BankHoliday, as : 'bank_holidays' }, - { model : model.LeaveType, as : 'leave_types' }, - ] - }); - }), - - Promise.try(function(){ - return this_user.getMy_leaves({ - where : { - $and : [ - { status : { $ne : sequelize.models.Leave.status_rejected() } }, - { status : { $ne : sequelize.models.Leave.status_canceled() } }, + Promise.try(() => + this_user.getMy_leaves({ + where: { + [Op.and]: [ + { status: { [Op.ne]: sequelize.models.Leave.status_rejected() } }, + { status: { [Op.ne]: sequelize.models.Leave.status_canceled() } } ], - $or : { - date_start : { - $between : [ - moment(year).startOf('year').format('YYYY-MM-DD'), - moment( - year.clone().add((is_multi_year ? 1 : 0), 'years') - ).endOf('year').format('YYYY-MM-DD'), + [Op.or]: { + date_start: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year.clone().add(is_multi_year ? 1 : 0, 'years')) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') ] }, - date_end : { - $between : [ - moment( year ).startOf('year').format('YYYY-MM-DD'), - moment( - year.clone().add((is_multi_year ? 1 : 0), 'years') - ).endOf('year').format('YYYY-MM-DD'), + date_end: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year.clone().add(is_multi_year ? 1 : 0, 'years')) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') ] } } - }, - }); - }), + } + }) + ), - Promise.try(function(){ - return this_user.promise_schedule_I_obey(); - }), + Promise.try(() => this_user.promise_schedule_I_obey()), - function(department, company, leaves, schedule){ - var leave_days = _.flatten( _.map(leaves, function(leave){ - return _.map( leave.get_days(), function(leave_day){ - leave_day.leave = leave; - return leave_day; - }); - })); + (department, company, leaves, schedule) => { + const leave_days = _.flatten( + _.map(leaves, leave => + _.map(leave.get_days(), leave_day => { + leave_day.leave = leave + return leave_day + }) + ) + ) return Promise.resolve( - _.map(months_to_show, function(month){ - return new CalendarMonth( - month, - { - bank_holidays : - department.include_public_holidays - ? _.map( - company.bank_holidays, - function(day){return day.date} - ) + _.map( + months_to_show, + month => + new CalendarMonth(month, { + bank_holidays: department.include_public_holidays + ? company.bank_holidays : [], - leave_days : leave_days, - schedule : schedule, - } - ); - }) - ); + leave_days, + schedule, + today: company.get_today(), + leave_types: company.leave_types + }) + ) + ) } + ) // End of join + } - ); // End of join - }; + this.validate_overlapping = function (new_leave_attributes) { + const this_user = this - - this.validate_overlapping = function(new_leave_attributes) { - var this_user = this; - - var days_filter = { - $between : [ + const days_filter = { + [Op.between]: [ new_leave_attributes.from_date, - moment(new_leave_attributes.to_date) - .add(1,'days').format('YYYY-MM-DD'), - ], - }; - - return this_user.getMy_leaves({ - where : { - $and : [ - { status : { $ne : sequelize.models.Leave.status_rejected() } }, - { status : { $ne : sequelize.models.Leave.status_canceled() } }, - ], - - $or : { - date_start : days_filter, - date_end : days_filter, - }, - }, - }) - - .then(function(overlapping_leaves){ + moment + .utc(new_leave_attributes.to_date) + .endOf('day') + .format('YYYY-MM-DD') + ] + } + console.log('validate_overlapping days_filter:', days_filter) + + return ( + this_user + .getMy_leaves({ + where: { + [Op.and]: [ + { status: { [Op.ne]: sequelize.models.Leave.status_rejected() } }, + { status: { [Op.ne]: sequelize.models.Leave.status_canceled() } } + ], - // Check there are overlapping leaves - if (overlapping_leaves.length === 0){ - return Promise.resolve(1); - } + [Op.or]: [ + { date_start: days_filter }, + { date_end: days_filter }, + { + [Op.and]: [ + { date_start: { [Op.lte]: new_leave_attributes.from_date } }, + { date_end: { [Op.gte]: new_leave_attributes.to_date } } + ] + } + ] + } + }) - var overlapping_leave = overlapping_leaves[0]; + // additional logging + .then(overlapping_leaves => { + console.log('Found overlapping leaves: ', overlapping_leaves) - if (overlapping_leave.fit_with_leave_request( - new_leave_attributes - )){ - return Promise.resolve(1); - } + // Check there are overlapping leaves + if (overlapping_leaves.length === 0) { + return Promise.resolve(1) + } - // Otherwise it is overlapping! - var error = new Error('Overlapping booking!'); - error.user_message = 'Overlapping booking!'; - throw error; + const overlapping_leave = overlapping_leaves[0] + console.log('Evaluating overlapping leave: ', overlapping_leave) - }); - }; // end of validate_overlapping + if (overlapping_leave.fit_with_leave_request(new_leave_attributes)) { + console.log('Overlapping leave fits with the new leave request') + return Promise.resolve(1) + } + // Otherwise it is overlapping! + const error = new Error('Overlapping booking!') + error.user_message = 'Overlapping booking!' + console.error('Overlapping booking detected: ', error) + throw error + }) + ) + } // Promise all leaves requested by current user, regardless // their statuses // - this.promise_my_leaves = function(args){ + this.promise_my_leaves = function (args) { + const self = this + // Time zone does not really matter here, although there could be issues + // around New Year (but we can tolerate that) + const year = args && args.year ? args.year : moment.utc().year() - var self = this, - where_clause = {}, - year = args.year || moment(); + const where_clause = [] + // console.log('promise_my_leaves args:', args) if (args && args.filter_status) { - where_clause = { status : args.filter_status }; + where_clause.push({ status: args.filter_status }) } - - if (args && ! args.ignore_year) { - where_clause['$or'] = { - date_start : { - $between : [ - moment(year).startOf('year').format('YYYY-MM-DD'), - moment(year).endOf('year').format('YYYY-MM-DD'), - ] - }, - date_end : { - $between : [ - moment(year).startOf('year').format('YYYY-MM-DD'), - moment(year).endOf('year').format('YYYY-MM-DD'), - ] + if (args && !args.ignore_year) { + where_clause.push({ + [Op.or]: { + date_start: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + }, + date_end: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + } } - }; + }) + } + // Case when there are start and end date defined + else if (args && args.dateStart && args.dateEnd) { + const { dateStart, dateEnd } = args + + where_clause.push({ + [Op.or]: [ + { + date_start: { + [Op.between]: [ + moment + .utc(dateStart) + .startOf('day') + .format('YYYY-MM-DD'), + moment + .utc(dateEnd) + .endOf('day') + .format('YYYY-MM-DD 23:59:59') + ] + } + }, + { + date_end: { + [Op.between]: [ + moment + .utc(dateStart) + .startOf('day') + .format('YYYY-MM-DD'), + moment + .utc(dateEnd) + .endOf('day') + .format('YYYY-MM-DD 23:59:59') + ] + } + }, + { + [Op.and]: [ + { + date_start: { + [Op.lte]: moment + .utc(dateStart) + .startOf('day') + .format('YYYY-MM-DD') + } + }, + { + date_end: { + [Op.gte]: moment + .utc(dateEnd) + .endOf('day') + .format('YYYY-MM-DD 23:59:59') + } + } + ] + } + ] + }) } - var promise_my_leaves = this.getMy_leaves({ + // Handle filter by id if provided + if (args && args.filter && args.filter.id) { + where_clause.push({ id: args.filter.id }) + } + + // console.log('promise_my_leaves where_clause:', where_clause.toString()) + const promise_my_leaves = this.getMy_leaves({ // TODO here is cartesian product between leave types and users, // needs to be split - include : [{ - model : sequelize.models.LeaveType, - as : 'leave_type', - },{ - model : sequelize.models.User, - as : 'user', - include : [{ - model : sequelize.models.Company, - as : 'company', - include : [{ - model : sequelize.models.BankHoliday, - as : 'bank_holidays', - }], - }], - }], - where : where_clause, - }) - - // Fetch approvers for each leave in separate query, to avoid cartesian - // products. - .then(function(leaves){ - - leaves.forEach(function(leave){ leave.user.cached_schedule = self.cached_schedule; }); - - return Promise.resolve(leaves) - .map( - function(leave){ - return leave.promise_approver() - .then(function(approver){ - leave.approver = approver; - return Promise.resolve(leave); - }); - }, { - concurrency : 10, - } - ); + include: [ + { + model: sequelize.models.LeaveType, + as: 'leave_type' + }, + { + model: sequelize.models.User, + as: 'user', + include: [ + { + model: sequelize.models.Company, + as: 'company', + include: [ + { + model: sequelize.models.BankHoliday, + as: 'bank_holidays' + } + ] + } + ] + } + ], + where: { [Op.and]: where_clause } }) - return promise_my_leaves; - }; + // Fetch approvers for each leave in separate query, to avoid cartesian + // products. + .then(leaves => { + leaves.forEach(leave => { + leave.user.cached_schedule = self.cached_schedule + }) + return Promise.resolve(leaves).map( + leave => + leave.promise_approver().then(approver => { + leave.approver = approver + return Promise.resolve(leave) + }), + { + concurrency: 10 + } + ) + }) - this.promise_my_active_leaves = function(args) { - var year = args.year || moment(); + .then(async leaves => { + const department = await self.getDepartment() + leaves.forEach(l => (l.user.department = department)) + return leaves + }) - return this.promise_my_leaves({ - year : year, - filter_status : [ - sequelize.models.Leave.status_approved(), - sequelize.models.Leave.status_new(), - sequelize.models.Leave.status_pended_revoke(), - ], - }); - }; + .then(leaves => LeaveCollectionUtil.promise_to_sort_leaves(leaves)) - // Promises leaves ever booked for current user - this.promise_my_active_leaves_ever = function() { + return promise_my_leaves + } - return this.promise_my_leaves({ - ignore_year : true, - filter_status : [ - sequelize.models.Leave.status_approved(), - sequelize.models.Leave.status_new(), - sequelize.models.Leave.status_pended_revoke(), - ], - }); - }; + // added fault-tolerance and logging + this.promise_my_active_leaves = function (args) { + try { + console.log('Executing promise_my_active_leaves with args:', args) + // Ensure year is either a properly formatted string or a JS Date object + const year = args && args.year ? args.year : moment.utc().year() - // Promise leaves that are needed to be Approved/Rejected - // - this.promise_leaves_to_be_processed = function(){ - return this.getSupervised_leaves({ - include : [{ - model : sequelize.models.LeaveType, - as : 'leave_type', - },{ - model : sequelize.models.User, - as : 'user', - include : [{ - model : sequelize.models.Company, - as : 'company', - include : [{ - model : sequelize.models.BankHoliday, - as : 'bank_holidays', - }], - },{ - model : sequelize.models.Department, - as : 'department', - }], - }], - where : { - status : [ + return this.promise_my_leaves({ + year, + filter_status: [ + sequelize.models.Leave.status_approved(), sequelize.models.Leave.status_new(), sequelize.models.Leave.status_pended_revoke() ] - }, - }) - .then(function(leaves){ - return Promise.resolve(leaves).map(function(leave){ - return leave.user.promise_schedule_I_obey(); - },{ - concurrency : 10, }) - .then(function(){ return Promise.resolve(leaves) }); - }); - }; // END of promise_leaves_to_be_processed + .then(leaves => { + console.log( + `Successfully retrieved and sorted leaves for year: ${year}` + ) + return LeaveCollectionUtil.promise_to_sort_leaves(leaves) + }) + .catch(err => { + console.error('Error in promise_my_leaves:', err) + throw err // Rethrow the error after logging + }) + } catch (err) { + console.error('Unexpected error in promise_my_active_leaves:', err) + throw err // Rethrow the error to ensure it's not silently ignored + } + } - this.promise_cancelable_leaves = function(){ - var self = this; + this.getMyActiveLeavesForDateRange = async function ({ dateStart, dateEnd }) { + // console.log('getMyActiveLeavesForDateRange dateStart:', dateStart, dateEnd) + const self = this - return self.promise_my_leaves({ - ignore_year : true, - filter_status : [ sequelize.models.Leave.status_new() ], + const rawLeaves = await self.promise_my_leaves({ + ignore_year: true, + dateStart, + dateEnd, + filter_status: [ + sequelize.models.Leave.status_approved(), + sequelize.models.Leave.status_new(), + sequelize.models.Leave.status_pended_revoke() + ] }) - .then(function(leaves){ - return Promise.map(leaves, function(leave){ - return leave.user.promise_schedule_I_obey(); - },{ - concurrency : 10, - }) - .then(function(){ return Promise.resolve(leaves) }); - }); - }; - - this.calculate_number_of_days_taken_from_allowance = function(args){ - var self = this, - leave_type = args ? args.leave_type : null, - leaves_to_traverse = this.my_leaves || []; + const leaves = await LeaveCollectionUtil.promise_to_sort_leaves(rawLeaves) + return leaves + } - leaves_to_traverse.forEach(function(leave){ leave.user.cached_schedule = self.cached_schedule; }); - - // If leave_type was provided, we care only about leaves of that type - if (leave_type) { - leaves_to_traverse = _.filter( - leaves_to_traverse, - function(leave){ return leave.leaveTypeId === leave_type.id; } - ); - } - - return _.reduce( - _.map( - _.filter( - leaves_to_traverse, - function (leave){ return leave.is_approved_leave(); } - ), - function(leave){ return leave.get_deducted_days_number(args); } - ), - function(memo, num){ return memo + num }, - 0 - ) || 0; - }; - + // Promises leaves ever booked for current user + this.promise_my_active_leaves_ever = function () { + return this.promise_my_leaves({ + ignore_year: true, + filter_status: [ + sequelize.models.Leave.status_approved(), + sequelize.models.Leave.status_new(), + sequelize.models.Leave.status_pended_revoke() + ] + }).then(leaves => LeaveCollectionUtil.promise_to_sort_leaves(leaves)) + } - // Based on leaves attached to the current user object, - // the method does not perform any additional queries + // Promise leaves that are needed to be Approved/Rejected // - this.get_leave_statistics_by_types = function(args){ - - if (! args ) args = {}; + this.promise_leaves_to_be_processed = function () { + const self = this + + return self + .promise_supervised_users() + .then(users => + sequelize.models.Leave.findAll({ + include: [ + { + model: sequelize.models.LeaveType, + as: 'leave_type' + }, + { + model: sequelize.models.User, + as: 'user', + include: [ + { + model: sequelize.models.Company, + as: 'company', + include: [ + { + model: sequelize.models.BankHoliday, + as: 'bank_holidays' + } + ] + }, + { + model: sequelize.models.Department, + as: 'department' + } + ] + } + ], + where: { + status: [ + sequelize.models.Leave.status_new(), + sequelize.models.Leave.status_pended_revoke() + ], + user_id: users.map(u => u.id) + } + }) + ) + .then(leaves => + Promise.resolve(leaves) + .map(leave => leave.user.promise_schedule_I_obey(), { + concurrency: 10 + }) + .then(() => Promise.resolve(leaves)) + .then(leaves => LeaveCollectionUtil.promise_to_sort_leaves(leaves)) + ) + } // END of promise_leaves_to_be_processed - var statistics = {}, - limit_by_top = args.limit_by_top || false; + this.promise_cancelable_leaves = function () { + const self = this - this.company.leave_types.forEach(function(leave_type){ - var initial_stat = { - leave_type : leave_type, - days_taken : 0, - }; - if (leave_type.limit && leave_type.limit > 0) { - initial_stat.limit = leave_type.limit; + return self + .promise_my_leaves({ + ignore_year: true, + filter_status: [sequelize.models.Leave.status_new()] + }) + .then(leaves => + Promise.map(leaves, leave => leave.user.promise_schedule_I_obey(), { + concurrency: 10 + }) + .then(() => Promise.resolve(leaves)) + .then(leaves => LeaveCollectionUtil.promise_to_sort_leaves(leaves)) + ) + } + + // count approved + this.calculate_number_of_days_taken_from_allowance = function (args) { + const leave_type = args ? args.leave_type : null + let leaves_to_traverse = this.my_leaves || [] + const count_personal_only = args ? args.count_personal : false + + // Set cached_schedule for each leave + leaves_to_traverse.forEach(leave => { + if (leave.user) { + leave.user.cached_schedule = this.cached_schedule } - statistics[leave_type.id] = initial_stat; - }); + }) - // Calculate statistics as an object - _.filter( - this.my_leaves, - function (leave){ return leave.is_approved_leave() } - ) - .forEach( - function(leave){ + // Filter leaves based on the conditions + if (count_personal_only) { + leaves_to_traverse = leaves_to_traverse.filter( + leave => leave.leave_type && leave.leave_type.use_personal + ) + } else if (leave_type) { + leaves_to_traverse = leaves_to_traverse.filter(leave => + leave_type.use_personal + ? leave.leave_type && leave.leave_type.use_personal + : leave.leaveTypeId === leave_type.id + ) + } - var stat_obj = statistics[leave.leave_type.id]; + // Calculate the sum of deducted days for approved leaves + const result = + leaves_to_traverse.reduce((memo, leave) => { + if (leave.is_approved_leave()) { + return memo + leave.get_deducted_days_number(args) + } + return memo + }, 0) || 0 - stat_obj.days_taken = stat_obj.days_taken + leave.get_deducted_days_number({ - ignore_allowance : true, - }); - } - ); + return result + } - var statistic_arr = _.map( - _.pairs(statistics), - function(pair){ - return pair[1]; - } - ); + // Based on leaves attached to the current user object, + // the method does not perform any additional queries + // + ; (this.get_leave_statistics_by_types = function (args) { + if (!args) args = {} - statistic_arr = _.sortBy( - statistic_arr, - 'days_taken' - ) - .reverse(); + const statistics = {} + const limit_by_top = args.limit_by_top || false + this.company.leave_types.forEach(leave_type => { + const initial_stat = { + leave_type, + days_taken: 0 + } + initial_stat.limit = leave_type.limit ? leave_type.limit : 0 + if (leave_type.use_personal) { + initial_stat.limit = this.department.personal + } + statistics[leave_type.id] = initial_stat + }) - if (limit_by_top) { - statistic_arr = _.first(statistic_arr, 4); - } + // Calculate statistics as an object + _.filter(this.my_leaves, function (leave) { + return leave.is_approved_leave() + }).forEach(function (leave) { + const stat_obj = statistics[leave.leave_type.id] - return _.sortBy(statistic_arr, function(rec){ return rec.leave_type.name; }); - }, + stat_obj.days_taken = + stat_obj.days_taken + + leave.get_deducted_days_number({ + ignore_allowance: true + }) + }) + let statistic_arr = _.map(_.pairs(statistics), function (pair) { + return pair[1] + }) - this.get_automatic_adjustment = function(args) { + statistic_arr = statistic_arr + .sort((a, b) => sorter(a.days_taken, b.days_taken)) + .reverse() - var now = (args && args.now) ? moment(args.now) : moment(); + if (limit_by_top) { + statistic_arr = _.first(statistic_arr, 4) + } - if ( - now.year() !== moment(this.start_date).year() - && ( ! this.end_date || moment(this.end_date).year() > now.year() ) - ){ - return 0; + return statistic_arr.sort((a, b) => + sorter(a.leave_type.name, b.leave_type.name) + ) + }), + + (this.promise_adjustment_and_carry_over_for_year = function(inputYear) { + const self = this; + + inputYear = inputYear || moment.utc(); + inputYear = moment.utc(inputYear).format('YYYY'); + + const startYear = moment.utc(self.start_date).format('YYYY'); + + // Define a recursive function to look back through previous years + function findAdjustment(year) { + if (year < startYear) { + // If no adjustment is found by the time we reach the start year, assume 0 + return Promise.resolve({ + adjustment: 0, + carried_over_allowance: 0 + }); } - var start_date = moment(this.start_date).year() === now.year() - ? moment(this.start_date) - : now.startOf('year'), - end_date = this.end_date && moment(this.end_date).year() <= now.year() - ? moment(this.end_date) - : moment().endOf('year'); + // Try to find an adjustment for the current year + return self.getAdjustments({ where: { year } }).then(adjustment_records => { + // By default there is no adjustments + const result = { + adjustment: 0, + carried_over_allowance: 0 + }; - return -1*(this.department.allowance - Math.round( - this.department.allowance * end_date.diff(start_date, 'days') / 365 - )); - }; + // If an adjustment is found, stop looking back + if (adjustment_records.length === 1) { + result.adjustment = adjustment_records[0].adjustment; + result.carried_over_allowance = adjustment_records[0].carried_over_allowance; + } else { + // If no adjustment is found for the current year, look back to the previous year + return findAdjustment(year - 1); + } + return Promise.resolve(result); + }); + } + + // Start looking back through previous years + return findAdjustment(inputYear); +}) + + this.promise_adjustment_for_year = function(year) { + const self = this + + return self + .promise_adjustment_and_carry_over_for_year(year) + .then(combined_record => Promise.resolve(combined_record.adjustment)) + } + + this.promise_carried_over_allowance_for_year = function (year) { + const self = this + + return self + .promise_adjustment_and_carry_over_for_year(year) + .then(combined_record => + Promise.resolve(combined_record.carried_over_allowance) + ) + } + + this.promise_to_update_adjustment = function (args) { + const self = this + const year = args.year || moment.utc().format('YYYY') + const adjustment = args.adjustment + + // Update related allowance adjustement record + return sequelize.models.UserAllowanceAdjustment.findOrCreate({ + where: { + user_id: self.id, + year + }, + defaults: { adjustment } + }).spread((record, created) => { + if (created) { + return Promise.resolve() + } - this.calculate_total_number_of_days_n_allowance = function(year) { + record.set('adjustment', adjustment) - // If optional paramater year was provided we need to calculate allowance - // for that year, and if it is something other then current year, - // we use department nominal allowance plus employee's allowance - // (ignoreing automatic allowance) - if (year && year != moment().year()) { - return this.department.allowance - + this.adjustment; - } + return record.save() + }) + } + + this.promise_to_update_carried_over_allowance = function (args) { + const self = this + const year = args.year || moment.utc().format('YYYY') + const carried_over_allowance = args.carried_over_allowance + + // Update related allowance adjustement record + return sequelize.models.UserAllowanceAdjustment.findOrCreate({ + where: { + user_id: self.id, + year + }, + defaults: { carried_over_allowance } + }).spread((record, created) => { + if (created) { + return Promise.resolve() + } - // Get general allowance based on department - return this.department.allowance - + this.get_automatic_adjustment() - // Adjust it based on current user - + this.adjustment; - }; + record.set('carried_over_allowance', carried_over_allowance) + return record.save() + }) + } - this.promise_my_leaves_for_calendar = function(args){ - var year = args.year || moment(); + this.promise_my_leaves_for_calendar = function (args) { + const year = args.year || this.company.get_today() return this.getMy_leaves({ - where : { - - $and : [ - { status : { $ne : sequelize.models.Leave.status_rejected() } }, - { status : { $ne : sequelize.models.Leave.status_canceled() } }, + where: { + [Op.and]: [ + { status: { [Op.ne]: sequelize.models.Leave.status_rejected() } }, + { status: { [Op.ne]: sequelize.models.Leave.status_canceled() } } ], - $or : { - date_start : { - $between : [ - moment(year).startOf('year').format('YYYY-MM-DD'), - moment(year).endOf('year').format('YYYY-MM-DD'), + [Op.or]: { + date_start: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') ] }, - date_end : { - $between : [ - moment(year).startOf('year').format('YYYY-MM-DD'), - moment(year).endOf('year').format('YYYY-MM-DD'), + date_end: { + [Op.between]: [ + moment + .utc(year) + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc(year) + .endOf('year') + .format('YYYY-MM-DD 23:59:59') ] } } - }, - }); // End of MyLeaves - }; + } + }) // End of MyLeaves + } // For given leave object (not necessary one with corresponding record in DB) // check if current user is capable to have it, that is if user's remaining @@ -500,89 +735,96 @@ module.exports = function(sequelize){ // // If validation fails an exceptionis thrown. // - this.validate_leave_fits_into_remaining_allowance = function(args){ - var self = this, - leave_type = args.leave_type, - leave = args.leave, + this.validate_leave_fits_into_remaining_allowance = function (args) { + const self = this + const leave_type = args.leave_type + const leave = args.leave // Derive year from Leave object - year = args.year || moment(leave.date_start); + const year = args.year || moment.utc(leave.date_start) // Make sure object contain all necessary data for that check - return self.reload_with_leave_details({ - year : year.clone(), - }) - .then(function(employee){ - return employee.reload_with_session_details(); - }) - .then(function(employee){ - return employee.company.reload_with_bank_holidays() - .then(function(){ return Promise.resolve(employee); }); - }) - .then(function(employee){ - - // Throw an exception when less than zero vacation would remain - // if we add currently requested absence - if ( - employee.calculate_number_of_days_available_in_allowance( - year.format('YYYY') - ) - - - leave.get_deducted_days_number({ - year : year.format('YYYY'), - user : employee, - leave_type : leave_type, + return ( + self + .reload_with_leave_details({ + year: year.clone() }) - < - 0 - ) { - - var error = new Error('Requested absence is longer than remaining allowance'); - error.user_message = error.toString(); - throw error; - } - - return Promise.resolve(employee); - }) - - // Check that adding new leave of this type will not exceed maximum limit of - // that type (if it is defined) - .then(function(employee){ - if ( - // There is a limit for current type - leave_type.limit - // ... and lemit is bigger than zero - && leave_type.limit > 0 - ) { - - // ... sum of used dayes for this limit is going to be bigger then limit - var would_be_used = employee.calculate_number_of_days_taken_from_allowance({ - year : year.format('YYYY'), - leave_type : leave_type, - ignore_allowance : true, + .then(employee => employee.reload_with_session_details()) + .then(employee => + employee.company + .reload_with_bank_holidays() + .then(() => Promise.resolve(employee)) + ) + .then(employee => + employee + .promise_allowance({ year }) + .then(allowance_obj => + Promise.resolve([ + allowance_obj.number_of_days_available_in_allowance, + employee + ]) + ) + ) + .then(args => { + const days_remaining_in_allowance = args[0] + const employee = args[1] + + const deducted_days = leave.get_deducted_days_number({ + year: year.format('YYYY'), + user: employee, + leave_type }) - + - leave.get_deducted_days_number({ - year : year.format('YYYY'), - user : employee, - leave_type : leave_type, - ignore_allowance : true, - }); - - if (would_be_used > leave_type.limit) { - var error = new Error('Adding requested '+leave_type.name - +" absense would exceed maximum allowed for such type by " - +(would_be_used - leave_type.limit) - ); + // Throw an exception when less than zero vacation would remain + // if we add currently requested absence + if (days_remaining_in_allowance - deducted_days < 0) { + const error = new Error( + 'Requested absence is longer than remaining allowance' + ) + error.user_message = error.toString() + throw error + } - error.user_message = error.toString(); - throw error; - } - } + return Promise.resolve(employee) + }) - return Promise.resolve(); - }); - }; + // Check that adding new leave of this type will not exceed maximum limit of + // that type (if it is defined) + .then(employee => { + let effectiveLimit = leave_type.limit ? leave_type.limit : 0 + if (leave_type.use_personal) { + effectiveLimit = employee.department.personal + } -}; + if (effectiveLimit && effectiveLimit > 0) { + const days_taken = employee.calculate_number_of_days_taken_from_allowance( + { + year: year.format('YYYY'), + leave_type, + ignore_allowance: true + } + ) + const days_deducted = leave.get_deducted_days_number({ + year: year.format('YYYY'), + user: employee, + leave_type, + ignore_allowance: true + }) + const would_be_used = days_taken + days_deducted + + // check if used days are going to be more than limit + if (would_be_used > effectiveLimit) { + const error = new Error( + 'Not enough (' + leave_type.name + ') days.' + // + (would_be_used - effectiveLimit) + ) + + error.user_message = error.toString() + throw error + } + } + return Promise.resolve() + }) + ) + } +} diff --git a/lib/model/mixin/user/company_aware.js b/lib/model/mixin/user/company_aware.js index 771e55147..55f1541f5 100644 --- a/lib/model/mixin/user/company_aware.js +++ b/lib/model/mixin/user/company_aware.js @@ -1,4 +1,3 @@ - /* * This is a role to be applied to user model that injects getters necessary * for fetching related company object with different level of details. @@ -8,178 +7,166 @@ * * */ -'use strict'; +'use strict' -var - Promise = require("bluebird"), - moment = require('moment'); +const Promise = require('bluebird') +const moment = require('moment') +const Sequelize = require('sequelize') +const Op = Sequelize.Op -module.exports = function(sequelize){ +const { sorter } = require('../../../util') +module.exports = function(sequelize) { /* Fetch company object associated with current user, the company object * includes all necessary associations for building user detail page * for user determined by user_id. * Returns promise that is resolved with company object as parameter */ - this.get_company_for_user_details = function(args){ - var user_id = args.user_id, - year = args.year || moment(), - current_user = this; - - return this.getCompany({ - include : [ - { - model : sequelize.models.User, - as : 'users', - where : { id : user_id }, - include : [ - - // Following is needed to be able to calculate how many days were - // taked from allowance - { - model : sequelize.models.Leave, - as : 'my_leaves', - required : false, - where : { - $or : { - date_start : { - $between : [ - moment().startOf('year').format('YYYY-MM-DD'), - moment().endOf('year').format('YYYY-MM-DD'), - ] + // + // TODO: Query below needs to be revisited as it is slow for users + // with many leaves + // + this.get_company_for_user_details = function(args) { + const user_id = args.user_id + const year = args.year || moment.utc() + const current_user = this + + return ( + this.getCompany({ + include: [ + { + model: sequelize.models.User, + as: 'users', + where: { id: user_id }, + include: [ + // Following is needed to be able to calculate how many days were + // taked from allowance + { + model: sequelize.models.Leave, + as: 'my_leaves', + required: false, + where: { + [Op.or]: { + date_start: { + [Op.between]: [ + moment + .utc() + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc() + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + }, + date_end: { + [Op.between]: [ + moment + .utc() + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc() + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + } + } + }, + include: [ + { + model: sequelize.models.LeaveType, + as: 'leave_type' }, - date_end : { - $between : [ - moment().startOf('year').format('YYYY-MM-DD'), - moment().endOf('year').format('YYYY-MM-DD'), + { + model: sequelize.models.User, + as: 'user', + include: [ + { + model: sequelize.models.Company, + as: 'company', + include: [ + { + model: sequelize.models.BankHoliday, + as: 'bank_holidays' + } + ] + } ] } - } + ] // End of my_leaves include }, - include : [{ - model : sequelize.models.LeaveType, - as : 'leave_type', - },{ - model : sequelize.models.User, - as : 'user', - include : [{ - model : sequelize.models.Company, - as : 'company', - include : [{ - model : sequelize.models.BankHoliday, - as : 'bank_holidays', - }], - }], - }] // End of my_leaves include - },{ - model : sequelize.models.Department, - as : 'department', + { + model: sequelize.models.Department, + as: 'department' + } + ] + }, + { + model: sequelize.models.Department, + as: 'departments', + include: { + model: sequelize.models.User, + as: 'manager' } - ], - },{ - model : sequelize.models.Department, - as : 'departments', - include : { - model : sequelize.models.User, - as : 'boss', } - } - ], - order : [ - [ - {model : sequelize.models.Department, as : 'departments'}, - sequelize.models.Department.default_order_field(), + ], + order: [ + [ + { model: sequelize.models.Department, as: 'departments' }, + sequelize.models.Department.default_order_field() + ] ] - ], - }) - - // Make sure that company got only one user associated with for - // provided user_id - .then(function(company){ - - if (!company || company.users.length !== 1) { - throw new Error( - 'User '+current_user.id+' tried to edit user '+user_id - +' but they do not share a company' - ); - } - - return Promise.resolve(company); - }); - }; + }) + + // Make sure that company got only one user associated with for + // provided user_id + .then(company => { + if (!company || company.users.length !== 1) { + throw new Error( + 'User ' + + current_user.id + + ' tried to edit user ' + + user_id + + ' but they do not share a company' + ) + } + return Promise.resolve(company) + }) + ) + } this.get_company_for_add_user = function() { - var model = sequelize.models; + const model = sequelize.models return this.getCompany({ - include : [ - {model : model.Department, as : 'departments'} - ], - order : [ + include: [{ model: model.Department, as: 'departments' }], + order: [ [ - {model : model.Department, as : 'departments'}, - model.Department.default_order_field(), + { model: model.Department, as: 'departments' }, + model.Department.default_order_field() ] - ], - }); - }; - - - this.get_company_with_all_users = function(){ - return this.getCompany({ - include : [ - { - model : sequelize.models.User, - as : 'users', - }, - ], - order : [ - [{ model : sequelize.models.User, as : 'users' }, 'lastname'] ] - }); - }; - + }) + } this.get_company_with_all_leave_types = function() { return this.getCompany({ - include : [{ - model : sequelize.models.LeaveType, - as : 'leave_types', - }], - order : [ - [{ model : sequelize.models.LeaveType, as : 'leave_types' }, 'name'] + include: [ + { + model: sequelize.models.LeaveType, + as: 'leave_types' + } + ], + order: [ + [ + { model: sequelize.models.LeaveType, as: 'leave_types' }, + 'sort_order', + 'DESC' + ], + [{ model: sequelize.models.LeaveType, as: 'leave_types' }, 'name'] ] - }); - }; - - - this.get_company_for_export = function(){ - return this.getCompany({ - include : [{ - model : sequelize.models.LeaveType, - as : 'leave_types', - },{ - model : sequelize.models.BankHoliday, - as : 'bank_holidays', - },{ - model : sequelize.models.Department, - as : 'departments', - },{ - model : sequelize.models.User, - as : 'users', - include : [{model: sequelize.models.Leave, as : 'my_leaves'}], - }], }) - .then(function(company){ - - // Erase password hash record as it is pottencially possible to reverse engineer - // the installatioon 'salt' - company.users.forEach(function(user){ user.password = ''; }); - - return Promise.resolve(company); - }); - }; - - -}; + } +} diff --git a/lib/model/team_view.js b/lib/model/team_view.js index 4030063b3..9fa03e9d7 100644 --- a/lib/model/team_view.js +++ b/lib/model/team_view.js @@ -1,90 +1,251 @@ +'use strict' -'use strict'; - -var moment = require('moment'), -Promise = require('bluebird'), -_ = require('underscore'); +const Promise = require('bluebird') +const Joi = require('joi') +const Exception = require('../error') +const _ = require('underscore') +const { sorter } = require('../util') function TeamView(args) { - var me = this; - - this.base_date = args.base_date || moment(); - this.user = args.user; + this.user = args.user + this.base_date = args.base_date || this.user.company.get_today() + + // Optional parameters that override base date specify months range + // Team view is going to represent. + // + // The precision is up to month, that is any smaller part of dates + // (such as days, hours etc) are ignored. + // + // If those two parameters are missed - base_date is used to determine with month + // Team view would represent. + // + this.start_date = args.start_date + this.end_date = args.end_date + + if (args.start_date && args.end_date && args.base_date) { + Exception.throw_user_error({ + user_error: 'Failed to calculate team view', + system_error: + 'TeamView could not be instanciated with start_date, end_data and base_date all defined.' + }) + } } -TeamView.prototype.promise_team_view_details = function(args){ - +TeamView.prototype.promise_team_view_details = function(args) { // Handle case when no parameters were provided - if ( ! args ) { - args = {}; + if (!args) { + args = {} } - var user = this.user, - current_department_id = args.department_id, // optional parameter - related_departments = [], - current_department, - base_date = this.base_date; - - var promise_departments; + const self = this + const user = this.user + const current_department_id = args.department_id // optional parameter + let related_departments = [] + let current_department + const base_date = this.base_date - var normalise_departments_func = function(my_department, supervised_departments){ + let promise_departments + function normalise_departments_func(my_department, supervised_departments) { if (my_department) { // Get all related departments by combining supervised ones with // one current user belongs to - supervised_departments.push(my_department); + supervised_departments.push(my_department) } - supervised_departments = _.uniq(supervised_departments, function(item){ return item.id }); + supervised_departments = _.uniq(supervised_departments, item => item.id) // Copy all available departments for current user into closured variable // to pass it into template - related_departments = _.sortBy(supervised_departments, 'name'); + related_departments = supervised_departments.sort((a, b) => + sorter(a.name, b.name) + ) // Find out what particular department is active now if (current_department_id) { - current_department = _.findWhere(supervised_departments, { id : Number(current_department_id) }); + current_department = _.findWhere(supervised_departments, { + id: Number(current_department_id) + }) } - return Promise.resolve(current_department ? [current_department] : supervised_departments); + return Promise.resolve( + current_department ? [current_department] : supervised_departments + ) } - if (user.is_admin() || user.company.share_all_absences) { - + if (user.is_admin() || user.is_manager() || user.company.share_all_absences) { // For admin users or if current company allows all users to see everybody's // time offs promise all departments for current company - promise_departments = user.company.getDepartments() - .then(function(departments){ - return normalise_departments_func(null, departments); - }); - + promise_departments = user.company + .getDepartments() + .then(departments => normalise_departments_func(null, departments)) } else { // Promise departments either supervised by current user or one that she belongs to promise_departments = Promise.join( user.getDepartment(), - user.getSupervised_departments(), + user.promise_supervised_departments(), normalise_departments_func - ); + ) } // Calculate users and leaves for every department - var promise_users_and_leaves = promise_departments.map(function(department){ - return department.promise_team_view({ base_date : base_date }); - }); + const promise_users_and_leaves = promise_departments.map(department => + self.start_date && self.end_date + ? department.promise_team_view_for_months_range( + self.start_date, + self.end_date + ) + : department.promise_team_view_for_month(base_date) + ) + + return promise_users_and_leaves.then(users_and_leaves => { + users_and_leaves = users_and_leaves + .flat(Infinity) + .sort((a, b) => + sorter(a.user.lastname + a.user.name, b.user.lastname + b.user.name) + ) - return promise_users_and_leaves.then(function(users_and_leaves){ + return Promise.resolve({ + users_and_leaves, + related_departments, + current_department + }) + }) +} - users_and_leaves = _.sortBy( - _.flatten(users_and_leaves), - function(item){ return item.user.lastname + item.user.name; } - ); +// Experimenting with parameter validation done with Joi.js +const inject_statistics_args_schema = Joi.object().keys({ + leave_types: Joi.array().items( + Joi.object().keys({ + id: Joi.number().required(), + use_allowance: Joi.boolean().required() + }) + ), + team_view_details: Joi.object() + .required() + .keys({ + users_and_leaves: Joi.array() + .required() + .items(Joi.object().keys()) + }) +}) + +/* + * Takes "team view details" and enrich them with statistics about absences + * each employee has for given month + * + * */ + +TeamView.prototype.inject_statistics = function(args) { + // Validate parameters + const param_validation = Joi.validate(args, inject_statistics_args_schema, { + allowUnknown: true + }) + if (param_validation.error) { + console.log( + 'An error occured when trying to validate args in inject_statistics.' + ) + console.dir(param_validation.error) + throw new Error( + 'Failed to validate parameters in TeamView.inject_statistics' + ) + } - return Promise.resolve({ - users_and_leaves : users_and_leaves, - related_departments : related_departments, - current_department : current_department, - }); - }); + const team_view_details = args.team_view_details + const leave_types = args.leave_types || [] + + // Convert leave types array into look-up map + const leave_types_map = {} + leave_types.forEach(lt => (leave_types_map[lt.id] = lt)) + + team_view_details.users_and_leaves.forEach(node => { + let deducted_days = 0 + + // Set statistics by leave type to zeros + const leave_type_stat = {} + leave_types.forEach(lt => (leave_type_stat[lt.id] = 0)) + + node.days + // Consider only those days that have any leave objects + .filter(day => !!day.leave_obj) + // Ignore those days which were not approved yet + .filter(day => !!day.leave_obj.is_approved_leave()) + // Ignore weekends + .filter(day => !day.is_weekend) + // Ignore bank holidays + .filter(day => !day.is_bank_holiday) + .forEach(day => { + if (day.is_leave_morning) { + leave_type_stat[day.morning_leave_type_id] = + leave_type_stat[day.morning_leave_type_id] + 0.5 + + if ( + leave_types_map[day.morning_leave_type_id] && + leave_types_map[day.morning_leave_type_id].use_allowance + ) { + deducted_days = deducted_days + 0.5 + } + } + + if (day.is_leave_afternoon) { + if ( + leave_types_map[day.afternoon_leave_type_id] && + leave_types_map[day.afternoon_leave_type_id].use_allowance + ) { + deducted_days = deducted_days + 0.5 + } + + leave_type_stat[day.afternoon_leave_type_id] = + leave_type_stat[day.afternoon_leave_type_id] + 0.5 + } + }) + + const statistics = { + deducted_days, + // Shows statistics by leave type + leave_type_break_down: { + // format for machine using + lite_version: leave_type_stat, + // format for rendering to end users + pretty_version: leave_types + // Sort by name + .sort((a, b) => sorter(a.name, b.name)) + .map(lt => ({ + name: lt.name, + stat: leave_type_stat[lt.id], + id: lt.id + })) + } + } + + node.statistics = statistics + }) + + return Promise.resolve(team_view_details) +} + +/* + * Take "team view details" and user for whom them were generated + * and ensure the details contain statisticts for only those employees + * current user has access to. + * + * */ + +TeamView.prototype.restrainStatisticsForUser = function(args) { + // TODO Consider parameters validation in the same fashion as in inject_statistics method -}; + const team_view_details = args.team_view_details + const observer_user = args.user + + const supervised_user_map = {} + observer_user.supervised_users.forEach(u => (supervised_user_map[u.id] = u)) + + team_view_details.users_and_leaves.forEach(item => { + if (item.statistics && !supervised_user_map[item.user.id]) { + delete item.statistics + } + }) + + return Promise.resolve(team_view_details) +} -module.exports = TeamView; +module.exports = TeamView diff --git a/lib/model/user_allowance.js b/lib/model/user_allowance.js new file mode 100644 index 000000000..cdb777449 --- /dev/null +++ b/lib/model/user_allowance.js @@ -0,0 +1,356 @@ +/* + * This class represent Employee's allowance. + * + * As allowance became quite complicated entity, which is calculated + * based on few sources, there was decision to move allowance calculation + * logic out into its own class. + * + * */ + +'use strict' + +const moment = require('moment') +const Promise = require('bluebird') +const Joi = require('joi') + +/* + * Section where we declare interfaces used in methods for this class. + * */ + +const schema_user = Joi.object().required() +const schema_year = Joi.object() + .type(moment) + // Consider removing defaults + .default(() => moment.utc(), 'Default year is current one') +const schema_now = Joi.object() + .type(moment) + // Consider removing defaults + // Note on this line: once it used to derive now from user's company + // get_now() method, but Joi.js has high penalty on defaults which + // use function with 'context' so I have removed it. + // Yes it is less precise but much more faster code. + .default(() => moment.utc(), 'Default is UTC one not from company specific') +const schema_promise_allowance = Joi.object() + .required() + .keys({ + year: schema_year, + user: schema_user, + now: schema_now, + /** + * If TRUE now is not going to be changed. + */ + forceNow: Joi.boolean().default(false) + }) +const scheme_constructor = Joi.object() + .required() + .keys({ + user: schema_user, + + number_of_days_taken_from_allowance: Joi.number().required(), + manual_adjustment: Joi.number().required(), + carry_over: Joi.number().required(), + nominal_allowance: Joi.number().required(), + nominal_personal: Joi.number().required(), + now: schema_now + }) + +/* + * Class definition. + * + * */ + +class UserAllowance { + constructor(args) { + // Validate provided parameters + args = Joi.attempt( + args, + scheme_constructor, + 'Failed parameters validation for UserAllowance constructor' + ) + + const self = this + + // console.log('args', args); + + // Private properties (not to be accessed directly) + self._user = args.user + self._number_of_days_taken_from_allowance = + args.number_of_days_taken_from_allowance + self._manual_adjustment = args.manual_adjustment + self._carry_over = args.carry_over + self._nominal_allowance = args.nominal_allowance + self._nominal_personal = args.nominal_personal + self._now = args.now + } + + get total_personal_available() { + const personal_taken = this._user.calculate_number_of_days_taken_from_allowance( + { + year: this._now.year(), + count_personal: true + } + ) + + return this._nominal_personal - personal_taken + } + + get total_personal_taken() { + const self = this + const personal_taken = self._user.calculate_number_of_days_taken_from_allowance( + { + year: self._now.year(), + count_personal: true + } + ) + + return personal_taken + } + + get total_number_of_days_in_allowance() { + const self = this + + return ( + self.nominal_allowance + + self.carry_over + + self.manual_adjustment + + self.employement_range_adjustment + ) + } + + get number_of_days_taken_from_allowance() { + return this._number_of_days_taken_from_allowance + } + + get manual_adjustment() { + return this._manual_adjustment + } + + get carry_over() { + return this._carry_over + } + + get nominal_allowance() { + return this._nominal_allowance + } + + get nominal_personal() { + return this._nominal_personal + } + + get number_of_regular_allowance() { + const self = this + if ( + self.user.start_date && + moment.utc(self.user.start_date).year() > self._now.year() + ) { + return 0 + } + + // regular non-personal days (total - personal) + const regular_allowance = + self.total_number_of_days_in_allowance - self.nominal_personal + + return regular_allowance + } + + get number_of_days_available_in_allowance() { + const self = this + + // Check case when user started after "now", then even so she has + // nominal allowance derived from general settings, she could not + // use it as it is valid for time she was not here + if ( + self.user.start_date && + moment.utc(self.user.start_date).year() > self._now.year() + ) { + return 0 + } + + return ( + self.total_number_of_days_in_allowance - + self.number_of_days_taken_from_allowance + + (self.is_accrued_allowance ? self.accrued_adjustment : 0) + ) + } + + get is_accrued_allowance() { + return !!this.user.department.is_accrued_allowance + } + + get user() { + return this._user + } + + get employement_range_adjustment() { + const self = this + const now = self._now.clone() + + if ( + now.year() !== moment.utc(self.user.start_date).year() && + (!self.user.end_date || + moment.utc(self.user.end_date).year() > now.year()) + ) { + return 0 + } + + // Determine start_date based on whether the start year is the current year + const start_date = + moment.utc(self.user.start_date).year() === now.year() + ? moment.utc(self.user.start_date) + : now.clone().startOf('year') + + // Determine end_date based on the user's end date and current year + const end_date = + self.user.end_date && moment.utc(self.user.end_date).year() <= now.year() + ? moment.utc(self.user.end_date) + : now.clone().endOf('year') + + // Calculate the difference in days and then the prorated allowance + const daysDifference = end_date.diff(start_date, 'days') + const proratedAllowance = Math.round( + (self.nominal_allowance * daysDifference) / 365 + ) + + // Calculate the remaining allowance after subtracting the prorated amount + const remainingAllowance = -1 * (self.nominal_allowance - proratedAllowance) + + return remainingAllowance // Return the calculated remaining allowance + } + + /* + * Accrued adjustment affects all total allowance components BUT carried + * over part. Because "carry over" part was already deserved. + * + * */ + get accrued_adjustment() { + const self = this + const now = self._now.clone() + + // Consider only following parts of allowance when calculating accrual + // adjustment: + // * nominal allowance + // * manual adjustment + // * adjustment based in start/end day of employment + // Other components do not make sense, e.g.: + // * carried over part - it could be immediately used as employee + // already worked on them last year + const allowance = + self.nominal_allowance + + self.manual_adjustment + + self.employement_range_adjustment + + const period_starts_at = + moment.utc(self.user.start_date).year() === now.year() + ? moment.utc(self.user.start_date) + : now.clone().startOf('year') + + const period_ends_at = + self.user.end_date && moment.utc(self.user.end_date).year() <= now.year() + ? moment.utc(self.user.end_date) + : now.clone().endOf('year') + + const days_in_period = period_ends_at.diff(period_starts_at, 'days') + + const delta = + (allowance * period_ends_at.diff(now, 'days')) / days_in_period + + return -1 * (Math.round(delta * 2) / 2).toFixed(1) + } + + /* + * The idea of this static method is to be constructor for UserAllowance class. + * It takes parameters and perform all necessary (and costly) actions to fetch + * all info required for allowance calculation for given user and year. + * */ + static promise_allowance(args) { + args = Joi.attempt( + args, + schema_promise_allowance, + 'Failed to validate parameters for promise_allowance' + ) + + const user = args.user + let year = args.year + let number_of_days_taken_from_allowance + let manual_adjustment + let carried_over_allowance + + const { forceNow, now } = args + + let flow = Promise.resolve() + + if (user.my_leaves === undefined) { + flow = flow.then(() => user.reload_with_leave_details({ year })) + } + + // Fetch adjustment and Carry over allowance + flow = flow.then(() => + user.promise_adjustment_and_carry_over_for_year(year) + ) + + flow = flow.then(adjustment_and_coa => { + manual_adjustment = adjustment_and_coa.adjustment + carried_over_allowance = adjustment_and_coa.carried_over_allowance + + // If no adjustment is found for the current year + if (!manual_adjustment) { + // Convert user.start_date to a moment object + const startYear = moment(user.start_date).year(); + let checkYear + + // Start looking back through previous years + for (let previousYear = year.year() - 1; previousYear >= startYear; previousYear--) { + checkYear = moment.utc(previousYear.toString(), 'YYYY') + adjustment_and_coa = user.promise_adjustment_and_carry_over_for_year(checkYear) + manual_adjustment = adjustment_and_coa.adjustment + + // If an adjustment is found, stop looking back + if (manual_adjustment) { + break + } + } + } + + // If no adjustment is found by the time we reach the start year, assume 0 + if (!manual_adjustment) { + manual_adjustment = 0 + } + + return Promise.resolve() + }) + + flow = flow.then(() => + Promise.resolve( + (number_of_days_taken_from_allowance = user.calculate_number_of_days_taken_from_allowance( + { + year: year.format('YYYY') + } + )) + ) + ) + + // Got all necessary data for UserAllowance object so build it! + flow = flow.then(() => { + const args = { + user, + manual_adjustment, + number_of_days_taken_from_allowance, + carry_over: carried_over_allowance, + nominal_allowance: user.department.allowance, + nominal_personal: user.department.personal + } + + if (forceNow && now) { + args.now = now + } else if (year && year.year() !== moment.utc().year()) { + args.now = year.startOf('year') + } + + return new UserAllowance(args) + }) + + return flow + } +} + +module.exports = UserAllowance diff --git a/lib/model/user_importer.js b/lib/model/user_importer.js new file mode 100644 index 000000000..3fa6579bb --- /dev/null +++ b/lib/model/user_importer.js @@ -0,0 +1,202 @@ +'use strict' + +const Joi = require('joi') +const uuid = require('node-uuid') +const Promise = require('bluebird') +const _ = require('lodash') +const Exception = require('../error') +const Models = require('./db') + +const add_user_interface_schema = Joi.object() + .required() + .keys({ + email: Joi.string().email(), + slack_username: Joi.string() + .optional() + .default(null) + .allow(null, ''), + lastname: Joi.string(), + name: Joi.string(), + company_id: Joi.number() + .integer() + .positive(), + department_id: Joi.number() + .integer() + .positive(), + + start_date: Joi.string().optional(), + end_date: Joi.string() + .default(null) + .allow(null), + admin: Joi.boolean().default(false), + manager: Joi.boolean().default(false), + auto_approve: Joi.boolean().default(false), + password: Joi.string().default(() => uuid.v4(), 'Populate default password') + }) + +function add_user(args) { + const validated_args = Joi.validate(args, add_user_interface_schema) + + if (validated_args.error) { + console.log('An error occured when validating parameters for add_user: ') + console.dir(validated_args) + Exception.throw_user_error({ + system_error: 'Failed to add new due to validation errors', + user_error: 'Failed to add user: ' + validated_args.error + }) + } + + // Use validated (and expanded) arguments object + args = validated_args.value + + const attributes = {} + const slack_username = args.slack_username + ? '@' + args.slack_username.replace('@', '') + : null + + attributes.email = args.email.toLowerCase() + attributes.slack_username = slack_username + attributes.lastname = args.lastname + attributes.name = args.name + attributes.company_id = args.company_id + attributes.department_id = args.department_id + + attributes.password = Models.User.hashify_password(args.password) + attributes.admin = args.admin + attributes.manager = args.manager + attributes.auto_approve = args.auto_approve + attributes.end_date = args.end_date + + // Pass start date inky if it is set, otherwise rely on database to use + // default value + if (args.start_date) { + attributes.start_date = args.start_date + } + + return ( + Promise.resolve() + + // Ensure given department ID is owned by given company ID + .then(() => + Models.Department.findOne({ + where: { id: args.department_id, company_id: args.company_id } + }).then(department => { + if (!department) { + Exception.throw_user_error({ + system_error: + 'Mismatch in department/company IDs when creating new user ' + + args.department_id + + '/' + + args.company_id, + user_error: 'Used wrong department' + }) + } + return Promise.resolve() + }) + ) + + // Ensure provided email is free to use + .then(() => validate_email_to_be_free({ email: args.email })) + + // Create new user record + .then(() => Models.User.create(attributes)) + ) +} + +async function add_users_in_bulk({ bulk_header, bulk_data, to_company_id }) { + const company = await Models.Company.scope('with_simple_departments').findOne( + { where: { id: to_company_id } } + ) + if (!company) throw new Error(`Company with ID ${to_company_id} not found`) + + // define indexes for vector columns + const department_vector_index = bulk_header.indexOf('department') + const email_vector_index = bulk_header.indexOf('email') + const slack_username_vector_index = bulk_header.indexOf('slack_username') + const lastname_vector_index = bulk_header.indexOf('lastname') + const name_vector_index = bulk_header.indexOf('name') + + // lights camera action + const dep_name_to_id = _.fromPairs( + company.departments.map(dep => [dep.name, dep.id]) + ) + const with_invalid_departments = bulk_data.filter( + vector => !dep_name_to_id[vector[department_vector_index]] + ) + if (with_invalid_departments.length > 0) { + const unknown_departments = with_invalid_departments + .map(vector => `"${vector[department_vector_index]}"`) + .join(', ') + throw new Error(`Unknown departments: ${unknown_departments}`) + } + + bulk_data.forEach(vector => { + vector[department_vector_index] = + dep_name_to_id[vector[department_vector_index]] + }) + + const users_or_errors = await Promise.map( + bulk_data, + async vector => { + const userData = { + email: vector[email_vector_index], + slack_username: vector[slack_username_vector_index], + lastname: vector[lastname_vector_index], + name: vector[name_vector_index], + department_id: vector[department_vector_index], + company_id: to_company_id + } + + try { + return await add_user(userData) + } catch (error) { + return { error, email: userData.email } + } + }, + { concurrency: 2 } + ) + + const result = { users: [], errors: [] } + users_or_errors.forEach(item => { + if (item.hasOwnProperty('error')) { + result.errors.push(item) + } else { + result.users.push(item) + } + }) + + return result +} + +const validate_email_to_be_free_schema = Joi.object() + .required() + .keys({ + email: Joi.string() + .email() + .required() + }) + +function validate_email_to_be_free(args) { + const validate_args = Joi.validate(args, validate_email_to_be_free_schema) + + if (validate_args.error) { + Exception.throw_user_error({ + system_error: 'validate_email_to_be_free failed arguments validation', + user_error: 'Failed to validate email' + }) + } + + return Models.User.find_by_email(args.email).then(user => { + if (user) { + Exception.throw_user_error('Email is already in use') + } + + return Promise.resolve() + }) +} + +module.exports = { + add_user, + add_users_in_bulk, + validate_email_to_be_free +} diff --git a/lib/passport.js b/lib/passport.js deleted file mode 100644 index 128b4a4ef..000000000 --- a/lib/passport.js +++ /dev/null @@ -1,164 +0,0 @@ - -/* - * Module to encapsulate logic for passport instantiation used for - * authentication. - * - * Exports function that return instance of passport object. - * - * */ - -'use strict'; - -var model = require('./model/db'), -passport = require('passport'), -Promise = require('bluebird'), -LocalStrategy = require('passport-local').Strategy; - -// In case if user is successfully logged in, make sure it is -// activated -function prepare_user_for_session(args) { - var user = args.user, - done = args.done; - - user.maybe_activate() - .then(function(user){ - return user.reload_with_session_details(); - }) - .then(function(){ - done(null, user); - }); -} - -// Function that performs authentication of given user object -// by given password. -// The method is callback based and the result is conveyed -// via provided callback function "done" -// -function authenticate_user(args){ - - var user = args.user, - password = args.password, - done = args.done, - email = user.email; - - // In case of LDAP authentification connect the LDAP server - if ( user.company.ldap_auth_enabled ) { - -// email = 'euler@ldap.forumsys.com'; password = 'password'; // TODO remove - Promise.resolve( user.company.get_ldap_server() ) - .then(function(ldap_server){ - - ldap_server.authenticate(email, password, function (err, u) { - if (err) { - console.log("LDAP auth error: %s", err); - return done(null, false); - } - prepare_user_for_session({ - user : user, - done : done, - }); - }); - - ldap_server.close(); - }) - .catch(function(error){ - console.error('Failed while trying to deal with LDAP server with error: %s', error); - - done(null, false); - }); - - // Provided password is correct - } else if (user.is_my_password(password)) { - - prepare_user_for_session({ - user : user, - done : done, - }); - - // User exists but provided password does not match - } else { - console.error( - 'When login user entered existsing email ' +email+ - ' but incorrect password' - ); - done(null, false); - } -} - -function strategy_handler(email, password, done) { - - // Normalize email to be in lower case - email = email.toLowerCase(); - - model.User - .find_by_email( email ) - .then(function(user){ - - // Case when no user for provided email - if ( ! user ) { - console.error( - 'At login: failed to find user with provided email %s', email - ); - - // We need to abort the execution of current callback function - // hence the return before calling "done" callback - return done(null, false); - } - - // Athenticate user by provided password - user.getCompany() - .then(function(company){ - - // We need to have company for user fetchef dow the line so query it now - user.company = company; - - authenticate_user({ - user : user, - password : password, - done : done, - }); - }); - }) - - // there was unknown error when trying to retrieve user object - .catch(function(error){ - console.error( - 'At login: unknown error when trying to login in as %s. Error: %s', - email, error - ); - - done(null, false); - }); -} - -module.exports = function(){ - - passport.use(new LocalStrategy( strategy_handler )); - - // Define how user object is going to be flattered into session - // after request is processed. - // In session store we save only user ID - passport.serializeUser(function(user, done) { - done(null, user.id); - }); - - // Defines how the user object is restored based on data saved - // in session storage. - // Fetch user data from DB based on ID. - passport.deserializeUser(function(id, done) { - - model.User.find({where : {id : id}}).then(function(user){ - return user.reload_with_session_details(); - }) - .then(function(user){ - done(null, user); - }) - .catch(function(error){ - console.error('Failed to fetch session user '+id+' with error: '+error); - - done(null, false, { message : 'Failed to fetch session user' }); - }); - }); - - return passport; -}; diff --git a/lib/passport/getCompanyAdminByToken.js b/lib/passport/getCompanyAdminByToken.js new file mode 100644 index 000000000..81379b4f6 --- /dev/null +++ b/lib/passport/getCompanyAdminByToken.js @@ -0,0 +1,37 @@ +'use strict' + +const { throwUserError } = require('../error') +const Promise = require('bluebird') + +module.exports = ({ token, model }) => { + if (!token) { + throwUserError({ + system_error: 'Provided token has FALSY value', + user_error: 'Wrong access token' + }) + } + + let action = model.Company.getCompanyByApiToken({ token }) + + action = action.then(company => { + if (!company) { + throwUserError({ + system_error: `Cannot find company record for provided token ${token}`, + user_error: 'Wrong access token' + }) + } + + const [adminUser] = company.get('users').filter(u => u.is_admin()) + + if (!adminUser) { + throwUserError({ + system_error: `Failed to find admin users for company ${company.id}`, + user_error: 'Wrong access token' + }) + } + + return Promise.resolve(adminUser) + }) + + return action +} diff --git a/lib/passport/index.js b/lib/passport/index.js new file mode 100644 index 000000000..d8a2ae7a5 --- /dev/null +++ b/lib/passport/index.js @@ -0,0 +1,258 @@ +/* + * Module to encapsulate logic for passport instantiation used for + * authentication. + * + * Exports function that return instance of passport object. + * + * */ + +'use strict' + +const model = require('../model/db') +const passport = require('passport') +const Promise = require('bluebird') +const LocalStrategy = require('passport-local').Strategy +const BearerStrategy = require('passport-http-bearer').Strategy +const getCompanyAdminByToken = require('./getCompanyAdminByToken') +const config = require('../config') + +// In case if user is successfully logged in, make sure it is +// activated +function prepare_user_for_session(args) { + const user = args.user + const done = args.done + + user + .maybe_activate() + .then(user => user.reload_with_session_details()) + .then(() => { + done(null, user) + }) +} + +// Function that performs authentication of given user object +// by given password. +// The method is callback based and the result is conveyed +// via provided callback function "done" +// +function authenticate_user({ user, password, done }) { + const email = user.email + + /* + * In case of LDAP authentification connect the LDAP server + */ + if (user.company.ldap_auth_enabled) { + // email = 'euler@ldap.forumsys.com'; password = 'password'; // TODO remove + Promise.resolve(user.company.get_ldap_server()) + .then(ldap_server => { + ldap_server.authenticate(email, password, (err, u) => { + ldap_server.close() + + if (err) { + console.log('LDAP auth error: %s', err) + return done(null, false) + } + + prepare_user_for_session({ + user, + done + }) + }) + + ldap_server.close() + }) + .catch(error => { + console.error( + 'Failed while trying to deal with LDAP server with error: %s', + error, + error.stack + ) + + done(null, false) + }) + + return + } + + /** + * Local authentication + */ + + // Check if the provided password is correct + if (!user.is_my_password(password)) { + console.error( + 'When login user entered existing email ' + + email + + ' but incorrect password' + ) + done(null, false) + return + } + + // Authenticate the user + prepare_user_for_session({ + user, + done + }) +} + +function strategy_handler(email, password, strategy, done) { + let authFunction + switch (strategy) { + case 'local': + authFunction = function({ user, password, done }) { + authenticate_user({ user, password, done }) + } + break + case 'google': + authFunction = function({ user, email, done }) { + let domain + domain = email.split('@') + if (domain && domain.length > 0) { + domain = domain[domain.length - 1] + } else { + domain = false + } + + if (!domain) { + return done('no auth domain') + } + + // check if valid domain + const validDomains = config.get('google:auth:domains') || false + if (!validDomains) { + console.error('no valid domains') + + return done('no valid domains for google set') + } + + if (validDomains.indexOf(domain) == -1) { + console.error('valid domains: ', validDomains, 'not valid: ', domain) + + return done('invalid auth domain') + } + + prepare_user_for_session({ + user, + done + }) + } + break + default: + return done('invalid strategy: ' + strategy) + } + + // Normalize email to be in lower case + email = email.toLowerCase() + + model.User.find_by_email(email) + .then(user => { + // Case when no user for provided email + if (!user) { + console.error( + 'At login: failed to find user with provided email %s', + email + ) + + // We need to abort the execution of current callback function + // hence the return before calling "done" callback + return done(null, false) + } + + // Athenticate user by provided password + user.getCompany().then(company => { + // We need to have company for user fetchef dow the line so query it now + user.company = company + + authFunction({ + user, + email, + password, + done + }) + }) + }) + + // there was unknown error when trying to retrieve user object + .catch(error => { + console.error( + 'At login: unknown error when trying to login in as %s. Error: %s', + email, + error, + error.stack + ) + + done(null, false) + }) +} + +module.exports = function() { + if (config.get('login:google')) { + const GoogleStrategy = require('passport-google-oauth20').Strategy + + passport.use( + new GoogleStrategy( + { + clientID: config.get('google:auth:clientid'), + clientSecret: config.get('google:auth:clientsecret'), + callbackURL: config.get('branding:url') + '/auth/google/callback' + }, + (accessToken, refreshToken, profile, done) => { + if (profile.emails && profile.emails.length > 0) { + const email = profile.emails[0].value + strategy_handler(email, false, 'google', done) + } else { + console.error('missing email in google reponse - scope?', profile) + done('no email found') + } + } + ) + ) + } + + passport.use( + new LocalStrategy((email, password, done) => { + strategy_handler(email, password, 'local', done) + }) + ) + + passport.use( + new BearerStrategy((token, done) => { + getCompanyAdminByToken({ token, model }) + .then(user => user.reload_with_session_details()) + .then(user => done(null, user)) + .catch(error => { + console.log(`Failed to authenticate TOKEN. Reason: '${error}'`) + done(null, false) + }) + }) + ) + + // Define how user object is going to be flattered into session + // after request is processed. + // In session store we save only user ID + passport.serializeUser((user, done) => { + done(null, user.id) + }) + + // Defines how the user object is restored based on data saved + // in session storage. + // Fetch user data from DB based on ID. + passport.deserializeUser((id, done) => { + model.User.findOne({ where: { id } }) + .then(user => user.reload_with_session_details()) + .then(user => { + done(null, user) + }) + .catch(error => { + console.error( + 'Failed to fetch session user ' + id + ' with error: ' + error, + error.stack + ) + + done(null, false, { message: 'Failed to fetch session user' }) + }) + }) + + return passport +} diff --git a/lib/route/api/index.js b/lib/route/api/index.js new file mode 100644 index 000000000..aa76ad6d8 --- /dev/null +++ b/lib/route/api/index.js @@ -0,0 +1,55 @@ +'use strict' + +const express = require('express') +const router = express.Router() + +const NOTIFICATION_TYPE_PENDING_REQUESTS = 'pending_request' + +/** + * Factory method that created a notification of given type + */ +const newNotification = ({ type, value }) => { + if (type === NOTIFICATION_TYPE_PENDING_REQUESTS) { + return { + type, + numberOfRequests: value, + label: + value === 1 + ? 'A leave request to process' + : `${value} leave requests to process`, + link: '/requests/' + } + } + + return null +} + +router.get('/notifications/', async (req, res) => { + const actingUser = req.user + + const data = [] + + try { + const leaves = await actingUser.promise_leaves_to_be_processed() + + if (leaves.length > 0) { + data.push( + newNotification({ + type: NOTIFICATION_TYPE_PENDING_REQUESTS, + value: leaves.length + }) + ) + } + + res.json({ data }) + } catch (error) { + console.log( + `Failed to fetch notifications for user [${actingUser.id}]: ${error} at ${ + error.stack + }` + ) + res.json({ error: 'Failed to fetch notifications.' }) + } +}) + +module.exports = router diff --git a/lib/route/audit.js b/lib/route/audit.js index 52d627d52..8d5394d15 100644 --- a/lib/route/audit.js +++ b/lib/route/audit.js @@ -1,107 +1,116 @@ +'use strict' -"usse strict"; - -var express = require('express'), -Bluebird = require('bluebird'), -validator = require('validator'), -_ = require('underscore'), -moment = require('moment'); -router = express.Router(), -Pager = require('./utils/pager')(); +const express = require('express') +const Bluebird = require('bluebird') +const validator = require('validator') +const _ = require('underscore') +const moment = require('moment') +const router = express.Router() +const Pager = require('./utils/pager')() // Make sure that current user is authorized to deal with settings -router.all(/.*/, require('../middleware/ensure_user_is_admin')); - -router.get(/email/, function(req, res){ - - var user_id = validator.toInt(req.param('user_id')), - start_date = validator.trim(req.param('start_date')), - end_date = validator.trim(req.param('end_date')), - page = validator.toInt(req.param('page')) || 1, - model = req.app.get('db_model'), - filter = { - company_id: req.user.companyId, - }; +router.all(/.*/, require('../middleware/ensure_user_is_admin')) + +router.get(/email/, (req, res) => { + const user_id = + typeof req.query.user_id !== 'undefined' && + validator.toInt(req.query.user_id) + let start_date = + typeof req.query.start_date !== 'undefined' && + validator.trim(req.query.start_date) + let end_date = + typeof req.query.end_date !== 'undefined' && + validator.trim(req.query.end_date) + const page = + (typeof req.query.page !== 'undefined' && + validator.toInt(req.query.page)) || + 1 + const model = req.app.get('db_model') + const filter = { + company_id: req.user.company_id + } - var items_per_page = Pager.items_per_page; + const items_per_page = Pager.items_per_page - if (start_date) start_date = req.user.company.normalise_date( start_date ); + if (start_date) start_date = req.user.company.normalise_date(start_date) // if there is a valid start date provided pass it to the filter - if ( validator.isDate(start_date) ) { - if (! filter.hasOwnProperty('created_at')) filter.created_at = {}; - filter.created_at['$gte'] = start_date; + if (start_date && validator.toDate(start_date)) { + if (!filter.hasOwnProperty('created_at')) filter.created_at = {} + filter.created_at.$gte = start_date } // ... same for end date - if (end_date) end_date = req.user.company.normalise_date( end_date ); + if (end_date) end_date = req.user.company.normalise_date(end_date) - if ( validator.isDate(end_date) ) { - if (! filter.hasOwnProperty('created_at')) filter.created_at = {}; - filter.created_at['$lte'] = end_date; + if (end_date && validator.toDate(end_date)) { + if (!filter.hasOwnProperty('created_at')) filter.created_at = {} + filter.created_at.$lte = end_date } - if ( validator.isInt(user_id) ) { - filter.user_id = user_id; + if ( + !isNaN(user_id) && + (typeof user_id === 'number' || (user_id && validator.isInt(user_id))) + ) { + filter.user_id = user_id } - var promise_emails = model.EmailAudit.findAndCountAll({ - where : filter, - - limit : items_per_page, - offset : items_per_page * (page - 1), - order : [ - [ 'id', 'DESC'] - ], + const promise_emails = model.EmailAudit.findAndCountAll({ + where: filter, - include : [{ - model : model.User, - as : 'user', - }] - }); + limit: items_per_page, + offset: items_per_page * (page - 1), + order: [['id', 'DESC']], - var promise_all_users = model.User.findAll({ - where : { - companyId : req.user.companyId, + include: [ + { + model: model.User, + as: 'user' + } + ] + }) + const promise_all_users = model.User.findAll({ + where: { + company_id: req.user.company_id }, - order : [ - ['lastname'] - ], - }); + order: [['lastname']] + }) Bluebird.join( promise_emails, promise_all_users, - function(email_result, all_users){ - - var filter = { - user_id : user_id, - }; + (email_result, all_users) => { + const filter = { + user_id + } if (start_date) { - filter.start_date = moment(start_date).format(req.user.company.get_default_date_format()); + filter.start_date = moment + .utc(start_date) + .format(req.user.company.get_default_date_format()) } if (end_date) { - filter.end_date = moment(end_date).format(req.user.company.get_default_date_format()); + filter.end_date = moment + .utc(end_date) + .format(req.user.company.get_default_date_format()) } res.render('audit/emails', { - audit_emails : email_result.rows, - all_users : all_users, - filter : filter, - show_reset_button : _.some([ user_id, start_date, end_date ]), - pager : Pager.get_pager_object({ - filter : filter, - total_items_count : email_result.count, - current_page : page, + audit_emails: email_result.rows, + all_users, + filter, + show_reset_button: _.some([user_id, start_date, end_date]), + pager: Pager.get_pager_object({ + filter, + total_items_count: email_result.count, + current_page: page }), - }); - + title: 'Email Audit | TimeOff' + }) } - ); -}); - + ) +}) -module.exports = router; +module.exports = router diff --git a/lib/route/bankHolidays.js b/lib/route/bankHolidays.js new file mode 100644 index 000000000..76b2d2918 --- /dev/null +++ b/lib/route/bankHolidays.js @@ -0,0 +1,276 @@ +'use strict' + +const express = require('express') +const moment = require('moment') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const config = require('../config') +const CalendarMonth = require('../model/calendar_month') +const Exception = require('../error') + +// Make sure that current user is authorized to deal with settings +router.all(/.*/, require('../middleware/ensure_user_is_admin')) + +const getCurrentYear = ({ req }) => { + const rawYear = req.query.year || req.body.year || '' + return (validator.isNumeric(rawYear) + ? moment.utc(rawYear, 'YYYY') + : req.user.company.get_today() + ).year() +} + +router.get('/bankholidays/', (req, res) => { + res.locals.custom_java_script.push('/js/bank_holidays.js') + + const currentYear = getCurrentYear({ req }) + const model = req.app.get('db_model') + req.user + .getCompany({ + include: [{ model: model.BankHoliday, as: 'bank_holidays' }], + order: [[{ model: model.BankHoliday, as: 'bank_holidays' }, 'date']] + }) + .then(company => { + const today = moment.utc() + const bankHolidays = company.bank_holidays + .filter(bh => moment.utc(bh.date).year() === currentYear) + .map(bh => ({ + id: bh.id, // Include the id property + // updated to include name + date: moment.utc(bh.date).format('YYYY-MM-DD'), + name: bh.name + })) + + // log map + console.log('mapped bh: ', bankHolidays) + + const calendar = [...Array(12).keys()] + .map(i => i + 1) + .map( + m => + new CalendarMonth(`${currentYear}-${String(m).padStart(2, '0')}`, { + today, + schedule: { + is_it_working_day: ({ day }) => moment.utc(day).isoWeekday() < 6 + }, + bank_holidays: bankHolidays + }) + ) + .map(cm => cm.as_for_template()) + + res.render('bankHolidays', { + company, + calendar, + bankHolidays, + yearCurrent: currentYear, + yearPrev: currentYear - 1, + yearNext: currentYear + 1, + startDateOfYearCurrent: moment.utc(currentYear, 'YYYY') + }) + }) +}) + +router.post('/bankholidays/', (req, res) => { + const model = req.app.get('db_model') + const currentYear = getCurrentYear({ req }) + + req.user + .getCompany({ + scope: ['with_bank_holidays'] + }) + .then(async company => { + const dateFormat = company.getDateFormat(); + + const normalizeDate = (date) => { + const formattedDate = moment(date, dateFormat, true); + if (!formattedDate.isValid()) { + throw new Error(`Invalid date format, expected format is ${dateFormat}`); + } + return formattedDate.format('YYYY-MM-DD'); // normalize to YYYY-MM-DD for storage + }; + + /** + * Validate input first + */ + const allBankHolidays = company.bank_holidays + .filter(bankHoliday => req.body[`date__${bankHoliday.id}`] !== undefined) + .map(bankHoliday => { + bankHoliday.name = req.body[`name__${bankHoliday.id}`] || bankHoliday.name; + bankHoliday.date = normalizeDate(req.body[`date__${bankHoliday.id}`]) || bankHoliday.date; + return bankHoliday; + }); + + // Add the new bank holiday to the list if exists. + if (req.body.name__new && validator.trim(req.body.name__new)) { + allBankHolidays.push({ + name: req.body.name__new, + date: normalizeDate(req.body.date__new), + company_id: company.id + }); + } + + // Validation + const invalidBankHolidays = allBankHolidays.filter(bankHoliday => { + const date = bankHoliday.date; + return !moment(date, 'YYYY-MM-DD', true).isValid(); // ensure the normalized date is valid + }); + + // Handle errors + if (invalidBankHolidays.length > 0) { + invalidBankHolidays.forEach(bankHoliday => + req.session.flash_error(`New day for ${bankHoliday.name} should be a valid date`) + ); + // If there were any validation errors: do not update bank holidays + // (it affects all bank holidays, that is if one failed + // validation - all bank holidays are not to be updated) + return Promise.reject(new Error('bad_request')); + } + + // All good, save all + return Promise.all([ + ...allBankHolidays.map(bankHoliday => { + // Create + if (!bankHoliday.id) { + return model.BankHoliday.create(bankHoliday); + } + + // Update + return bankHoliday.update({ + name: bankHoliday.name, + date: bankHoliday.date + }); + }) + ]); + }) + .then(() => { + req.session.flash_message('Changes to bank holidays were saved!'); + res.redirect_with_session(`/settings/bankholidays/?year=${currentYear}`); + }) + .catch(error => { + console.error(`An error occurred when trying to edit Bank holidays by user [${req.user.id}]: ${error}`); + console.log(error); + + req.session.flash_error('Failed to update bank holidays, please contact customer service'); + res.redirect_with_session(`/settings/bankholidays/?year=${currentYear}`); + }); +}) + +router.post('/bankholidays/import/', (req, res) => { + const model = req.app.get('db_model') + const config_countries = config.get('countries') + const currentYear = getCurrentYear({ req }) + + Promise.try(() => + req.user.getCompany({ + scope: ['with_bank_holidays'] + }) + ) + .then(company => { + // re-organize existing bank holiday in look up map manner + const existing_bank_holidays_map = {} + company.bank_holidays.forEach(bh => { + existing_bank_holidays_map[company.normalise_date(bh.date)] = 1 + }) + + // Fetch all default bank holidays known for current country + let bank_holidays_to_import = + config_countries[company.country || 'GB'].bank_holidays + + // prepare list of bank holidays that needs to be added + bank_holidays_to_import = bank_holidays_to_import + + // Ignore those which dates already used + .filter( + bh => + !existing_bank_holidays_map.hasOwnProperty( + company.normalise_date(bh.date) + ) + ) + // Ignore those from another years + .filter(bh => moment.utc(bh.date).year() === currentYear) + // and transform bank holidays into import friendly structure + .map(bh => ({ name: bh.name, date: bh.date, company_id: company.id })) + + return model.BankHoliday.bulkCreate(bank_holidays_to_import) + }) + .then(created_bank_holidays => { + if (!created_bank_holidays.length || created_bank_holidays.length <= 0) { + req.session.flash_message('No more new bank holidays exist') + return + } + + req.session.flash_message( + 'New bank holidays were added: ' + + created_bank_holidays.map(bh => bh.name).join(', ') + ) + }) + .then(() => + res.redirect_with_session(`/settings/bankholidays/?year=${currentYear}`) + ) + .catch(error => { + console.log( + 'An error occurred when trying to import default bank holidays by user ' + + req.user.id + ) + console.dir(error) + + if (error && error.tom_error) { + req.session.flash_error(Exception.extract_user_error_message(error)) + } + + req.session.flash_error('Failed to import bank holidays') + }) +}) + +router.post('/bankholidays/delete/:bankHolidayId/', (req, res) => { + const currentYear = getCurrentYear({ req }) + const bankHolidayId = req.params.bankHolidayId + + if ( + typeof bankHolidayId !== 'number' && + (!bankHolidayId || !validator.isInt(bankHolidayId)) + ) { + console.error( + `User ${req.user.id} submitted non-INT bank holiday ID ${bankHolidayId}` + ) + req.session.flash_error('Cannot remove bank holiday: wrong parameters') + return res.redirect_with_session( + `/settings/bankholidays/?year=${currentYear}` + ) + } + + req.user + .getCompany({ + scope: ['with_bank_holidays'] + }) + .then(company => { + const bankHolidayToRemove = company.bank_holidays.find( + bh => String(bh.id) === String(bankHolidayId) + ) + + // Check if user specify valid department number + if (!bankHolidayToRemove) { + return Promise.reject(new Error('Unable to remove the bank holiday.')) + } + + return bankHolidayToRemove.destroy() + }) + .then(() => { + req.session.flash_message('Bank holiday was successfully removed') + return res.redirect_with_session( + `/settings/bankholidays/?year=${currentYear}` + ) + }) + .catch(error => { + console.error( + `User ${req.user.id + } tried to remove non-existing bank holiday number ${bankHolidayId}` + ) + console.log('Error: ', error) + req.session.flash_error('Cannot remove bank holiday: wrong parameters') + + res.redirect_with_session(`/settings/bankholidays/?year=${currentYear}`) + }) +}) + +module.exports = router diff --git a/lib/route/calendar.js b/lib/route/calendar.js index 15d4fed30..a4135de5d 100644 --- a/lib/route/calendar.js +++ b/lib/route/calendar.js @@ -1,47 +1,66 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const Promise = require('bluebird') +const moment = require('moment') +const _ = require('underscore') +const validator = require('validator') +const get_and_validate_leave_params = require('./validator/leave_request') +const TeamView = require('../model/team_view') +const EmailTransport = require('../email') +const SlackTransport = require('../slack') + +const { + createNewLeave, + getLeaveForUserView, + doesUserHasExtendedViewOfLeave +} = require('../model/leave') +const { leaveIntoObject } = require('../model/Report') +const { getCommentsForLeave } = require('../model/comment') +const { sorter } = require('../util') + +router.post('/bookleave/', (req, res) => { + Promise.join( + req.user.promise_users_I_can_manage(), + req.user.get_company_with_all_leave_types(), + Promise.try(() => get_and_validate_leave_params({ req, params: req.body })), + (users, company, valid_attributes) => { + // Make sure that indexes submitted map to existing objects + const employee = users[valid_attributes.user] || req.user + const [leave_type] = company.leave_types.filter( + lt => `${lt.id}` === `${valid_attributes.leave_type}` + ) + + if (!employee) { + req.session.flash_error('Incorrect employee') + throw new Error('Got validation errors') + } -"use strict"; - -var express = require('express'), - router = express.Router(), - Promise = require('bluebird'), - moment = require('moment'), - _ = require('underscore'), - validator = require('validator'), - get_and_validate_leave_params = require('./validator/leave_request'), - TeamView = require('../model/team_view'), - CalendarMonth = require('../model/calendar_month'), - EmailTransport = require('../email'); - -router.post('/bookleave/', function(req, res){ - var model = req.app.get('db_model'); - - Promise.join ( - req.user.promise_users_I_can_manage(), - req.user.get_company_with_all_leave_types(), - Promise.try( function(){return get_and_validate_leave_params({req : req})}), - function(users, company, valide_attributes){ - // Make sure that indexes submitted map to existing objects - var employee = users[valide_attributes.user] || req.user, - leave_type = company.leave_types[valide_attributes.leave_type]; - - if (!employee) { - req.session.flash_error('Incorrect employee'); - throw new Error( 'Got validation errors' ); - } - - if (!leave_type) { - req.session.flash_error('Incorrect leave type'); - throw new Error( 'Got validation errors' ); - } - - return model.Leave.create_new_leave({ - for_employee : employee, - of_type : leave_type, - with_parameters : valide_attributes, - }); - } - ) + if (!leave_type) { + req.session.flash_error('Incorrect leave type') + throw new Error('Got validation errors') + } + + if (company.is_mode_readonly_holidays()) { + req.session.flash_error( + 'Company account is locked and new Timeoff ' + + 'requests could not be added. Please contact administration.' + ) + throw new Error('Company is in "Read-only holidays" mode') + } + + const leave = createNewLeave({ + for_employee: employee, + of_type: leave_type, + with_parameters: valid_attributes + }) + return leave + } + ) + .then(leave => leave.reloadWithAssociates()) + /* .then(function(leave){ return leave.reload({ include : [ @@ -51,191 +70,362 @@ router.post('/bookleave/', function(req, res){ ], }); }) - .then(function(leave){ - var Email = new EmailTransport(); - - return Email.promise_leave_request_emails({ - leave : leave, - }); - + */ + .then(leave => { + const Slack = new SlackTransport() + + Slack.promise_leave_request_slacks({ + leave + }) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send leave slack message to: ' + error, + error.stack + ) + }) + + const Email = new EmailTransport() + + Email.promise_leave_request_emails({ + leave + }) // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send leave send_mail to: ' + error, + error, + error.stack + ) + }) + + return Promise.resolve(leave) }) - .then(function(){ - - req.session.flash_message('New leave request was added'); - return res.redirect_with_session( - req.param('redirect_back_to') - ? req.param('redirect_back_to') - : '../' - ); + .then(() => { + req.session.flash_message('New leave request was added') + return res.redirect_with_session( + req.body.redirect_back_to ? req.body.redirect_back_to : '../' + ) }) - .catch(function(error){ - console.error( - 'An error occured when user '+req.user.id+ - ' try to create a leave request: '+error - ); - req.session.flash_error('Failed to create a leave request'); - if (error.hasOwnProperty('user_message')) { - req.session.flash_error(error.user_message); - } - return res.redirect_with_session( - req.param('redirect_back_to') - ? req.param('redirect_back_to') - : '../' - ); - }); - -}); - -router.get('/', function(req, res) { - - // Add JS that is specific only to current page - res.locals.custom_java_script.push( - '/js/inittooltips.js' - ); - - var current_year = validator.isNumeric(req.param('year')) - ? moment(req.param('year'), 'YYYY') - : moment(); - - var show_full_year = validator.toBoolean(req.param('show_full_year')); + .catch(error => { + console.error( + 'An error occured when user ' + + req.user.id + + ' try to create a leave request: ', + error, + error.stack + ) + req.session.flash_error('Failed to create a leave request') + if (error.user_message) { + req.session.flash_error(error.user_message) + } + return res.redirect_with_session( + req.body.redirect_back_to ? req.body.redirect_back_to : '../' + ) + }) +}) + +router.get('/', (req, res) => { + const current_year = + req.query.year && validator.isNumeric(req.query.year) + ? moment.utc(req.query.year, 'YYYY') + : req.user.company.get_today() + + const show_full_year = + (req.query.show_full_year && + validator.toBoolean(req.query.show_full_year)) || + false Promise.join( req.user.promise_calendar({ - year : current_year.clone(), - show_full_year : show_full_year, + year: current_year.clone(), + show_full_year }), req.user.get_company_with_all_leave_types(), - req.user.reload_with_leave_details({ year : current_year }), - req.user.promise_superviser(), - function(calendar, company, user, superviser){ - var full_leave_type_statistics = user.get_leave_statistics_by_types(); + req.user.reload_with_leave_details({ year: current_year }), + req.user.promise_supervisors(), + req.user.promise_allowance({ year: current_year }), + (calendar, company, user, supervisors, user_allowance) => { + const full_leave_type_statistics = user.get_leave_statistics_by_types() res.render('calendar', { - calendar : _.map(calendar, function(c){return c.as_for_template()}), - company : company, - title : 'Calendar', - current_user : user, - superviser : superviser, - previous_year : moment(current_year).add(-1,'year').format('YYYY'), - current_year : current_year.format('YYYY'), - next_year : moment(current_year).add(1,'year').format('YYYY'), - show_full_year : show_full_year, - leave_type_statistics : _.filter(full_leave_type_statistics, function(st){ - return st.days_taken > 0; - }), - full_leave_type_statistics : full_leave_type_statistics, - this_year : moment().format('YYYY'), - }); + calendar: _.map(calendar, c => c.as_for_template()), + company, + title: 'Calendar | TimeOff', + current_user: user, + supervisors, + previous_year: moment + .utc(current_year) + .add(-1, 'year') + .format('YYYY'), + current_year: current_year.format('YYYY'), + next_year: moment + .utc(current_year) + .add(1, 'year') + .format('YYYY'), + show_full_year, + leave_type_statistics: _.filter( + full_leave_type_statistics, + st => st.days_taken > 0 + ), + + // User allowance object is simple object with attributes only + user_allowance + }) } - ); + ) +}) -}); +router.get('/teamview/', async (req, res) => { + const user = req.user -router.get('/teamview/', function(req, res){ + if (user.company.is_team_view_hidden && (!user.admin && !user.manager)) { + return res.redirect_with_session('/') + } - // Add JS that is specific only to current page - res.locals.custom_java_script.push( - '/js/inittooltips.js' - ); + const base_date = + req.query.date && validator.toDate(req.query.date) + ? moment.utc(req.query.date) + : req.user.company.get_today() + + const grouped_mode = getGroupedModeParameter(req) + const current_deparment_id = getDepartmentIdForTeamView(req) + const team_view = new TeamView({ user, base_date }) + + try { + const [team_view_details, company] = await Promise.all([ + team_view.promise_team_view_details({ + department_id: current_deparment_id + }), + user.get_company_with_all_leave_types() + ]) + + // Enrich "team view details" with statistics as how many deducted days each employee spent current month + const team_view_details_with_stat = await team_view.inject_statistics({ + team_view_details, + leave_types: company.leave_types + }) - var base_date = validator.isDate(req.param('date')) - ? moment(req.param('date')) - : moment(); + const { + users_and_leaves, + related_departments, + current_department + } = await team_view.restrainStatisticsForUser({ + user, + team_view_details: team_view_details_with_stat + }) - var team_view = new TeamView({ - base_date : base_date, - user : req.user, - }); + const renderingContext = { + company, + users_and_leaves, + related_departments, + current_department, + base_date, + prev_date: moment.utc(base_date).add(-1, 'month'), + next_date: moment.utc(base_date).add(1, 'month') + } - var current_deparment_id = validator.isNumeric(req.param('department')) - ? req.param('department') - : null; + if (grouped_mode) { + renderingContext.grouped_mode = true + renderingContext.users_and_leaves_by_departments = groupUsersOnTeamViewByDepartments( + users_and_leaves + ) + } - Promise.join( - team_view.promise_team_view_details({ - department_id : current_deparment_id, - }), - req.user.get_company_with_all_leave_types(), - function(team_view_details, company){ - - return res.render('team_view', { - base_date : base_date, - prev_date : moment(base_date).add(-1,'month'), - next_date : moment(base_date).add(1,'month'), - users_and_leaves : team_view_details.users_and_leaves, - related_departments : team_view_details.related_departments, - current_department : team_view_details.current_department, - company : company, - }); + res.render('team_view', renderingContext) + } catch (error) { + console.error( + `An error occurred when user ${ + user.id + } tried to access TeamView page: ${error}, at ${error.stack}` + ) + req.session.flash_error( + 'Failed to access TeamView page. Please contact administrator.' + ) - }); + if (error.user_message) { + req.session.flash_error(error.user_message) + } -}); + return res.redirect_with_session('/') + } +}) -router.get('/feeds/', function(req, res){ - req.user - .getFeeds() - .then(function(feeds){ - - return Promise.join( - promise_feed_of_type({user : req.user, feeds: feeds, type : 'calendar'}), - promise_feed_of_type({user : req.user, feeds: feeds, type : 'teamview'}), - promise_feed_of_type({user : req.user, feeds: feeds, type : 'company'}), - function(calendar_feed, team_view_feed, company_feed){ - res.render('feeds_list', { - title : 'My feeds', - calendar_feed : calendar_feed, - team_view_feed: team_view_feed, - current_host : req.get('host'), - }); - }); +const getGroupedModeParameter = req => { + /** + * grouped_mode parameter is saved in the current session so user's + * transition between different pages does not reset the value + */ + let groupedMode = !!req.query.grouped_mode + + if (req.query.save_grouped_mode) { + req.session.teamViewGroupedMode = groupedMode + } + + // for cases when no grouped_mode parameter was supplied: used onf from session + if (req.query.grouped_mode === undefined) { + groupedMode = req.session.teamViewGroupedMode + } + + return groupedMode +} + +const getDepartmentIdForTeamView = req => { + /** + * department parameter is saved in the current session so user's + * transition between different pages does not reset the value + */ + + let departmentId = + req.query.department && validator.isNumeric(req.query.department) + ? req.query.department + : null + + if (req.query.save_current_department) { + req.session.teamViewDepartmentId = departmentId + } - }); -}); + // for cases when no grouped_mode parameter was supplied: used onf from session + if (req.query.department === undefined) { + departmentId = req.session.teamViewDepartmentId + } + + return departmentId +} + +const groupUsersOnTeamViewByDepartments = usersAndLeaves => { + const departmentsDict = usersAndLeaves.reduce( + (acc, item) => ({ + ...acc, + [item.user.department.id]: { + departmentName: item.user.department.name, + users_and_leaves: [] + } + }), + {} + ) + + usersAndLeaves.forEach(item => { + departmentsDict[item.user.department.id].users_and_leaves.push(item) + }) + + return Object.values(departmentsDict).sort((a, b) => + sorter(a.departmentName, b.departmentName) + ) +} + +router.get('/feeds/', (req, res) => { + req.user.getFeeds().then(feeds => + Promise.join( + promise_feed_of_type({ user: req.user, feeds, type: 'calendar' }), + promise_feed_of_type({ user: req.user, feeds, type: 'teamview' }), + (calendar_feed, team_view_feed) => { + res.render('feeds_list', { + title: 'My feeds | TimeOff', + calendar_feed, + team_view_feed, + current_host: req.get('host') + }) + } + ) + ) +}) -router.post('/feeds/regenerate/', function(req, res){ - var model = req.app.get('db_model'); +router.post('/feeds/regenerate/', (req, res) => { + const model = req.app.get('db_model') req.user .getFeeds() - .then(function(feeds){ - var the_feed = _.findWhere(feeds, { feed_token : req.param('token') }); + .then(feeds => { + const the_feed = _.findWhere(feeds, { feed_token: req.body.token }) if (the_feed) { - return model.UserFeed.promise_new_feed({ - user : req.user, - type : the_feed.type, - }); + user: req.user, + type: the_feed.type + }) } - return Promise.resolve(); + return Promise.resolve() + }) + .then(() => { + req.session.flash_message('Feed was regenerated') + return res.redirect_with_session('/calendar/feeds/') }) - .then(function(){ - req.session.flash_message('Feed was regenerated'); - return res.redirect_with_session('/calendar/feeds/'); - }); -}); +}) // Fetch or create new feed feed provided types function promise_feed_of_type(args) { - var type = args.type, - user = args.user, - feeds= args.feeds, - feed = _.findWhere(feeds, { type : type }), - feed_promise; + const type = args.type + const user = args.user + const feeds = args.feeds + const feed = _.findWhere(feeds, { type }) + let feed_promise - if (! feed) { + if (!feed) { feed_promise = user.sequelize.models.UserFeed.promise_new_feed({ - user : user, - type : type, - }); + user, + type + }) } else { - feed_promise = Promise.resolve( feed ); + feed_promise = Promise.resolve(feed) } - return feed_promise; -}; + return feed_promise +} + +router.get('/leave-summary/:leaveId/', async (req, res) => { + const actingUser = req.user + const leaveId = validator.trim(req.params.leaveId) + const dbModel = req.app.get('db_model') + try { + const leave = await getLeaveForUserView({ actingUser, leaveId, dbModel }) + console.log('getting details for leave', leaveId) + const extendedView = await doesUserHasExtendedViewOfLeave({ + user: actingUser, + leave + }) + console.log('extendedView: ', extendedView) + if (extendedView) { + const user = await leave.getUser() + await user.promise_schedule_I_obey() + const [extendedLeave] = await user.promise_my_leaves({ + // changed this + ignore_year: true, + filter: { id: leave.id } + }) + const leaveDetails = leaveIntoObject(extendedLeave) + console.log( + 'leaveIntoObject leaveDetails: id, start, end, deducted', + leaveId, + leaveDetails.startDate, + leaveDetails.endDate, + leaveDetails.deductedDays + ) + const comments = await getCommentsForLeave({ leave }) + leaveDetails.commentsString = comments + .map(({ comment }) => comment) + .join('
    ') + + return res.render('leave/popup_leave_details', { + leave: leaveDetails, + layout: false + }) + } else { + // return res.send('Short'); + const leaveDetails = leaveIntoObject(leave) + return res.render('leave/popup_leave_details', { + leave: leaveDetails, + layout: false, + limitedView: true + }) + } + } catch (error) { + console.log( + `Failed to obtain Leave [${leaveId}] summary: ${error} at ${error.stack}` + ) + return res.send('Failed to get leave details...') + } +}) -module.exports = router; +module.exports = router diff --git a/lib/route/dashboard.js b/lib/route/dashboard.js index 20ae697a8..19eb62e7b 100644 --- a/lib/route/dashboard.js +++ b/lib/route/dashboard.js @@ -2,40 +2,36 @@ * * */ -"use strict"; +'use strict' -var express = require('express'); -var router = express.Router(); +const express = require('express') +const router = express.Router() -router.get('/', function(req, res) { +router.get('/', (req, res) => { + const user = req.user - var user = req.user; + req.session.keep_old() - req.session.keep_old(); + // if no user available in session show main public + if (!user) { + return res.redirect_with_session('./login/') + } - // if no user available in session show main public - if (!user) { - return res.redirect_with_session('./login/'); - } - - return res.redirect_with_session('./calendar/'); -}); + return res.redirect_with_session('./calendar/') +}) // Make sure that all fallowing handlers Dashboard // require authenticated users -router.all(/.*/, function (req, res, next) { - - if ( !req.user ) { - return res.redirect_with_session(303, '/'); - } - - next(); -}); - -router.get('/foo/', function(req, res) { +router.all(/.*/, (req, res, next) => { + if (!req.user) { + return res.redirect_with_session(303, '/') + } - res.render('dashboard', { title: 'FOO' }); -}); + next() +}) +router.get('/foo/', (_req, res) => { + res.render('dashboard', { title: 'FOO' }) +}) -module.exports = router; +module.exports = router diff --git a/lib/route/departments.js b/lib/route/departments.js new file mode 100644 index 000000000..07a9765b3 --- /dev/null +++ b/lib/route/departments.js @@ -0,0 +1,633 @@ +'use strict' + +const { sorter } = require('../util') + +const express = require('express') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const _ = require('underscore') + +// Make sure that current user is authorized to deal with settings +router.all(/.*/, require('../middleware/ensure_user_is_admin')) + +// fill dropdown for personal days +function generate_department_personal_days() { + const personal_days_options = [{ value: 0, caption: 'None' }] + let personal_days = 1 + + while (personal_days <= 10) { + personal_days_options.push({ value: personal_days, caption: personal_days }) + personal_days += 1 + } + + return personal_days_options +} + +function generate_all_department_allowances() { + const allowance_options = [{ value: 0, caption: 'None' }] + let allowance = 0.5 + + while (allowance <= 50) { + allowance_options.push({ value: allowance, caption: allowance }) + allowance += 0.5 + } + + return allowance_options +} + +function get_and_validate_department(args) { + const req = args.req + const session = req.session + const index = args.suffix + const company = args.company + // If no_suffix is set then parameter names are considered without "indexes" + const no_suffix = args.no_suffix + const department_name = args.department_name + + function getParam(name) { + return req.body[no_suffix ? name : name + '__' + index] + } + + // Get user parameters + const name = getParam('name') ? validator.trim(getParam('name')) : '' + const allowance = getParam('allowance') + ? validator.trim(getParam('allowance')) + : '' + const personal = getParam('personal') + ? validator.trim(getParam('personal')) + : '' + const manager_id = getParam('manager_id') + ? validator.trim(getParam('manager_id')) + : '' + const include_public_holidays = getParam('include_public_holidays') + ? validator.toBoolean(getParam('include_public_holidays')) + : false + const is_accrued_allowance = getParam('is_accrued_allowance') + ? validator.toBoolean(getParam('is_accrued_allowance')) + : false + + // Validate provided parameters + // + // New allowance should be from range of (0;50] + if (!validator.isFloat(allowance)) { + session.flash_error( + 'New allowance for ' + department_name + ' should be numeric' + ) + } else { + const allowanceNumber = parseFloat(allowance) + if (!(allowanceNumber > 0 && allowanceNumber <= 50)) { + session.flash_error( + 'New allowance for ' + + department_name + + ' should be between 0.5 and 50 days' + ) + } + } + // New personal days should be from range of (0;10] + if (!validator.isFloat(personal)) { + session.flash_error( + 'New personal allowance for ' + department_name + ' should be numeric' + ) + } else { + const personalNumber = parseFloat(personal) + if (!(personalNumber > 0 && personalNumber <= 10)) { + session.flash_error( + 'New personal allowance for ' + + department_name + + ' should be between 0.5 and 10 days' + ) + } + } + // New manager ID should be numeric and from within + // current company + if ( + typeof manager_id !== 'number' && + (!manager_id || !validator.isNumeric(manager_id)) + ) { + session.flash_error( + 'New manager reference for ' + department_name + ' should be numeric' + ) + } else if ( + !_.contains( + _.map(company.users, user => String(user.id)), + String(manager_id) + ) + ) { + req.session.flash_error( + 'New manager for ' + department_name + ' is unknown' + ) + } + + return { + allowance, + personal, + manager_id, + include_public_holidays, + is_accrued_allowance, + name + } +} + +router.get('/departments/', (req, res) => { + // Add JS that is specific only to current page + res.locals.custom_java_script.push('/js/departments.js') + + let company_for_template + const model = req.app.get('db_model') + + req.user + .getCompany({ + scope: ['with_active_users', 'order_by_active_users'] + }) + .then(company => { + company_for_template = company + return company.getDepartments({ + scope: ['with_simple_users', 'with_manager'], + order: [[model.Department.default_order_field()]] + }) + }) + .then(departments => { + res.render('departments_overview', { + title: 'Settings - Departments | TimeOff', + departments: departments.sort((a, b) => sorter(a.name, b.name)), + allowance_options: generate_all_department_allowances(), + personal_days_options: generate_department_personal_days(), + company: company_for_template + }) + }) +}) + +router.post('/departments/', (req, res) => { + const model = req.app.get('db_model') + + req.user + .getCompany({ + scope: ['with_active_users'] + }) + .then(company => { + const attributes = get_and_validate_department({ + req, + params: req.body, + session: req.session, + suffix: 'new', + company, + department_name: 'New department' + }) + + if (req.session.flash_has_errors()) { + return Promise.resolve(1) + } + + attributes.company_id = company.id + + return model.Department.create(attributes) + }) + + .then(() => { + if (!req.session.flash_has_errors()) { + req.session.flash_message('Changes to departments were saved') + } + + return res.redirect_with_session('/settings/departments/') + }) + + .catch(error => { + console.error( + 'An error occurred when trying to add department by user ' + + req.user.id + + ' : ', + error, + error.stack + ) + + req.session.flash_error( + 'Failed to add new department, please contact customer service' + ) + + return res.redirect_with_session('/settings/departments/') + }) +}) + +router.post('/departments/delete/:department_id/', (req, res) => { + const department_id = req.params.department_id + let department_to_remove + console.log('deleting department: ' + department_id) + + if ( + typeof department_id !== 'number' && + (!department_id || !validator.isInt(department_id)) + ) { + console.error( + 'User ' + req.user.id + ' submited non-int department ID ' + department_id + ) + + req.session.flash_error('Cannot remove department: wronge parameters') + + return res.redirect_with_session('/settings/departments/') + } + + req.user + .getCompany() + + .then(company => + company.getDepartments({ + scope: ['with_simple_users'], + where: { + id: department_id + } + }) + ) + .then(departments => { + department_to_remove = departments[0] + + // Check if user specify valid department number + if (!department_to_remove) { + req.session.flash_error('Cannot remove department: wronge parameters') + + throw new Error( + 'User ' + + req.user.id + + ' tried to remove non-existing department ID' + + department_id + ) + } + + if (department_to_remove.users.length > 0) { + req.session.flash_error( + 'Cannot remove department ' + + department_to_remove.name + + ' as it still has ' + + department_to_remove.users.length + + ' users.' + ) + + throw new Error('Department still has users') + } + + // TODO VPP remove corresponding records in supervisors linking table + return department_to_remove.destroy() + }) + .then(() => { + req.session.flash_message('Department was successfully removed') + return res.redirect_with_session('/settings/departments/') + }) + .catch(error => { + console.error( + 'An error occurred when trying to edit departments by user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + return res.redirect_with_session( + department_to_remove + ? '/settings/departments/edit/' + department_to_remove.get('id') + '/' + : '/settings/departments/' + ) + }) +}) + +function promise_to_extract_company_and_department(req, only_active = true) { + const department_id = + req.body.department_id || + req.query.department_id || + req.params.department_id + let company + + return Promise.try(() => { + if ( + typeof department_id !== 'number' && + (!department_id || !validator.isInt(department_id)) + ) { + throw new Error( + 'User ' + + req.user.id + + ' tried to open department refered by non-int ID ' + + department_id + ) + } + + if (only_active) { + return req.user.getCompany({ + scope: ['with_active_users', 'order_by_active_users'] + }) + } else { + return req.user.getCompany({ + scope: ['with_all_users'] + }) + } + }) + .then(c => { + company = c + + if (!company) { + throw new Error('Cannot determin company!') + } + + return company.getDepartments({ + scope: ['with_simple_users', 'with_manager', 'with_supervisors'], + where: { + id: department_id + } + }) + }) + .then(departments => { + const department = departments[0] + + // Ensure we have database record for given department ID + if (!department) { + throw new Error('Non existing department ID provided') + } + + return Promise.resolve({ + company, + department + }) + }) +} + +router.get('/departments/edit/:department_id/', (req, res) => { + const department_id = req.params.department_id + console.log('editing department: ' + department_id) + + Promise.try(() => promise_to_extract_company_and_department(req)) + .then(result => { + const department = result.department + const company = result.company + + res.render('department_details', { + title: 'Settings - Department details | TimeOff', + department, + company, + allowance_options: generate_all_department_allowances(), + personal_days_options: generate_department_personal_days() + }) + }) + .catch(error => { + console.error( + 'An error occurred when trying to edit department ' + + department_id + + ' for user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + req.session.flash_error('Failed to fetch details for given department') + + return res.redirect_with_session('/settings/departments/') + }) +}) + +/** + * Handles the POST request to edit a department. + * + * This route is responsible for updating the details of a department, including + * adding or removing supervisors. It first extracts the company and department + * information from the request, then performs the appropriate update operation + * based on the request body. + * + * If the request body includes a `remove_supervisor_id`, it will remove the + * specified supervisor from the department. If the `do_add_supervisors` flag is + * set, it will update the supervisors for the department. Otherwise, it will + * update the department details directly. + * + * After the update is complete, it redirects the user back to the department + * details page. + * + * @param {Object} req - The Express request object. + * @param {Object} res - The Express response object. + */ +router.post('/departments/edit/:department_id/', (req, res) => { + const department_id = req.body.department_id + let company + let department + + Promise.try(() => promise_to_extract_company_and_department(req)) + .then(result => { + company = result.company + department = result.department + + // Update include_holiday and is_accrued from req.body + department.include_holiday = req.body.include_holiday + department.is_accrued = req.body.is_accrued + + return Promise.resolve(1) + }) + + .then(() => { + if (req.body.remove_supervisor_id) { + return promise_to_remove_supervisor({ + supervisor_id: req.body.remove_supervisor_id, + company, + department + }).then(() => { + req.session.flash_message( + 'Supervisor was removed from ' + department.name + ) + return Promise.resolve(1) + }) + } else if (req.body.do_add_supervisors) { + return promise_to_update_supervisors({ + req, + company, + department + }).then(() => { + req.session.flash_message( + 'Supervisors were added to department ' + department.name + ) + return Promise.resolve(1) + }) + } + + return promise_to_update_department({ + req, + company, + department + }).then(() => { + req.session.flash_message( + 'Department ' + department.name + ' was updated' + ) + return Promise.resolve(1) + }) + }) + + .then(() => res.redirect_with_session('.')) + + .catch(error => { + console.error( + 'An error occurred when trying to update secondary superwisors for depertment ' + + department_id + + ' by user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + req.session.flash_error("Failed to update department's details") + + return res.redirect_with_session('../../') + }) +}) + +function promise_to_remove_supervisor(args) { + const supervisor_id = args.supervisor_id + const company = args.company + const department = args.department + + // Make sure that provided supervisor ID belongs to user from current company + if ( + company.users.map(u => String(u.id)).indexOf(String(supervisor_id)) === -1 + ) { + return Promise.resolve(1) + } + + return department.Model.sequelize.models.DepartmentSupervisor.destroy({ + where: { + department_id: department.id, + user_id: supervisor_id + } + }) +} + +function promise_to_update_supervisors(args) { + const req = args.req + const company = args.company + const department = args.department + console.log('updating supervisors for department:', department.id) + + // added .body here + let supervisor_ids = req.body.supervisor_id || [] + console.log('pre-filtered supervisor ids:' + supervisor_ids) + + // Take list of all users as a base of intersection, + // so we use submitted data only as criteria and do not save it in database + supervisor_ids = company.users + .map(user => user.id) + .filter(id => supervisor_ids.indexOf(String(id)) !== -1) + + console.log('filtered supervisor ids:' + supervisor_ids) + + const link_model = department.Model.sequelize.models.DepartmentSupervisor + + return link_model + .findAll({ + where: { + department_id: department.id + }, + attributes: ['user_id'], + raw: true + }) + .then(existingSupervisors => { + const existingSupervisorIds = existingSupervisors.map( + supervisor => supervisor.user_id + ) + + // Find supervisors to remove + const supervisorsToRemove = existingSupervisorIds.filter( + id => !supervisor_ids.includes(id) + ) + + // Find supervisors to add + const supervisorsToAdd = supervisor_ids.filter( + id => !existingSupervisorIds.includes(id) + ) + + // Remove supervisors + const removePromises = supervisorsToRemove.map(id => + link_model.destroy({ + where: { + department_id: department.id, + user_id: id + } + }) + ) + + // Add supervisors + const addPromises = supervisorsToAdd.map(id => + link_model.create({ + user_id: id, + department_id: department.id + }) + ) + + return Promise.all([...removePromises, ...addPromises]) + }) +} + +function promise_to_update_department(args) { + const req = args.req + const company = args.company + const department = args.department + + const attributes = get_and_validate_department({ + company, + department_name: department.name, + no_suffix: true, + req + }) + + // If there were any validation errors: do not update department + if (req.session.flash_has_errors()) { + throw new Error( + 'Invalid parameters submitted while while attempt to update department details' + ) + } + + return department.update(attributes) +} + +router.get('/departments/available-supervisors/:department_id/', (req, res) => { + const department_id = req.params.department_id + let department + let company + + Promise.try(() => promise_to_extract_company_and_department(req)) + .then(result => { + department = result.department + company = result.company + + return department.promise_me_with_supervisors() + }) + + .then(department_with_supervisors => { + const supervisor_map = {} + + department_with_supervisors.supervisors.forEach(user => { + supervisor_map[user.id] = true + }) + + console.log('supervisor map: ', supervisor_map) + + res.render('department/available_supervisors', { + layout: false, + users: _.map( + _.filter(company.users, user => user.id !== department.manager_id), + user => { + user._marked = supervisor_map[user.id] + return user + } + ), + title: 'Settings - Department Available Supervisors | TimeOff' + }) + }) + .catch(error => { + console.error( + 'An error occurred when trying to get all availabele superviers for department ' + + department_id + + ' for user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + res.send('REQUEST FAILED') + }) +}) + +module.exports = router diff --git a/lib/route/feed.js b/lib/route/feed.js index ff3908584..fcff4aca0 100644 --- a/lib/route/feed.js +++ b/lib/route/feed.js @@ -1,128 +1,255 @@ - -"use strict;"; - -var express = require('express'), -router = express.Router(), -_ = require('underscore'), -moment = require('moment'), -Promise = require('bluebird'), -ical = require('ical-generator'), -TeamView = require('../model/team_view'); - -router.get('/:token/ical.ics', function(req, res){ - - var cal = ical({ - domain : 'timeoff.management', - }), - token = req.param('token'), - model = req.app.get('db_model'), - user; +'use strict' + +const express = require('express') +const router = express.Router() +const _ = require('underscore') +const moment = require('moment') +const Promise = require('bluebird') +const ical = require('ical-generator') +const config = require('../config') +const TeamView = require('../model/team_view') + +const numberOfFutureMonthsInTeamViewFeed = + config.get('number_of_future_months_in_team_view_feed') || 6 +const numberOfPastMonthsInTeamViewFeed = + config.get('number_of_past_months_in_team_view_feed') || 2 + +const { getCommentsForLeave } = require('../model/comment') + +router.get('/:token/anniversary.ics', (req, res) => { + const cal = ical({ + domain: 'timeoff.management' + }) + const token = req.params.token + const model = req.app.get('db_model') Promise.resolve() - .then(function(){ - return model.UserFeed.find({ - where : {feed_token : token}, - include : [ + .then(() => + model.UserFeed.find({ + where: { feed_token: token }, + include: [ { - model : model.User, - as : 'user', - include : [{ - model : model.Company, - as : 'company', - }] + model: model.User, + as: 'user', + include: [ + { + model: model.Company, + as: 'company' + } + ] } ] - }); - }) - .then(function(feed){ - - if ( ! feed ) { - throw new Error("Unknown token provided"); + }) + ) + .then(feed => { + if (!feed) { + throw new Error('Unknown token provided') } - user = feed.user; - - if (feed.is_calendar()){ - return user.promise_calendar({ - year : moment(), - show_full_year : true, + return model.User.findAll({ + where: { + company_id: feed.user.company.id, + $or: [{ end_date: { $lt: new Date() } }, { end_date: null }] + } + }) + .then(users => { + cal.name('anniversary') + + const days = users.map(u => { + // next one + const nextOne = new Date(u.start_date) + const year0 = nextOne.getFullYear() + const yearNow = new Date().getFullYear() + nextOne.setFullYear(yearNow) + const years = yearNow - year0 // moment().diff(u.start_date, 'years'); + return { + user: u, + moment: nextOne, + years + } + }) + + return days }) - .then(function(calendar){ - - cal.name(user.full_name() + ' calendar'); - - var days = _.flatten( - _.map(calendar, function(cal){ return cal.as_for_team_view(); }) - ); + .then(days => { + days.forEach(day => { + const start = moment.utc(day.moment) + const end = moment.utc(day.moment) + + cal.createEvent({ + start: start.toDate(), + end: end.toDate(), + summary: + day.user.full_name() + + ' is ' + + day.years + + ' years at the company.', + allDay: true, + description: new Buffer( + JSON.stringify({ years: day.years, name: day.user.full_name() }) + ).toString('base64') + }) + }) + + res.send(cal.toString()) + }) + }) + .catch(err => { + console.error(err) + res.status(404).send('N/A') + }) +}) - days.forEach(function(day){ day.user = user; }); +router.get('/:token/ical.ics', (req, res) => { + const cal = ical({ + domain: 'timeoff.management' + }) + const token = req.params.token + const model = req.app.get('db_model') + let user - return Promise.resolve(days); - }); - } else { + Promise.resolve() + .then(() => + model.UserFeed.findOne({ + where: { feed_token: token }, + include: [ + { + model: model.User, + as: 'user', + include: [ + { + model: model.Company, + as: 'company', + where: { + mode: { $ne: model.Company.get_mode_readonly_holidays() } + } + } + ] + } + ] + }) + ) + .then(feed => { + if (!feed) { + throw new Error('Unknown token provided') + } - cal.name(user.full_name() + ' team'); + user = feed.user - var team_view = new TeamView({ - user : user, - }); + if (feed.is_calendar()) { + cal.name(user.full_name() + ' calendar') + return user + .promise_calendar({ + year: user.calendar.company.get_today(), + show_full_year: true + }) + .then(calendar => { + cal.name(user.full_name() + ' calendar') - return team_view.promise_team_view_details() - .then(function(details){ - var days = []; + const days = _.flatten( + _.map(calendar, cal => cal.as_for_team_view()) + ) - details.users_and_leaves.forEach(function(rec){ - var user = rec.user; - rec.days.forEach(function(day){ - day.user = user; - }); - days = days.concat( rec.days ); - }); + days.forEach(day => { + day.user = user + }) - return Promise.resolve(days); - }); + return Promise.resolve(days) + }) + } else { + cal.name(`${user.full_name()}'s team whereabout`) + + // Get the list of month deltas in relation to current month, to we can calculate + // moths for which to build the feed + const monthDeltas = Array.from( + Array(numberOfFutureMonthsInTeamViewFeed + 1).keys() + ).concat( + Array.from(Array(numberOfPastMonthsInTeamViewFeed).keys()).map( + i => -1 * (i + 1) + ) + ) + + return Promise.resolve( + monthDeltas.map(delta => + user.company + .get_today() + .clone() + .add(delta, 'months') + .startOf('month') + ) + ) + .map( + month => { + const team_view = new TeamView({ + user, + base_date: month + }) + + return team_view.promise_team_view_details().then(details => { + let days = [] + + details.users_and_leaves.forEach(rec => { + const user = rec.user + rec.days.forEach(day => { + day.user = user + }) + days = days.concat(rec.days) + }) + + return Promise.resolve(days) + }) + }, + { concurrency: 10 } + ) + .then(arrayOfDays => Promise.resolve(_.flatten(arrayOfDays))) } }) - .then(function(days){ - - days.forEach(function(day){ - + .then(async days => { + for (const day of days) { // We care only about days when employee is on leave if (!(day.is_leave_morning || day.is_leave_afternoon)) { - return; + continue } - var start = moment(day.moment), - end = moment(day.moment); + const start = moment.utc(day.moment) + const end = moment.utc(day.moment) + let allDay = false if (day.is_leave_morning && day.is_leave_afternoon) { - start.hour(9).minute(0); - end.hour(17).minute(0); + start.hour(9).minute(0) + end.hour(17).minute(0) + allDay = true } else if (!day.is_leave_morning && day.is_leave_afternoon) { - start.hour(13).minute(0); - end.hour(17).minute(0); + start.hour(13).minute(0) + end.hour(17).minute(0) } else if (day.is_leave_morning && !day.is_leave_afternoon) { - start.hour(9).minute(0); - end.hour(13).minute(0); + start.hour(9).minute(0) + end.hour(13).minute(0) } - cal.createEvent({ - start : start.toDate(), - end : end.toDate(), - summary : day.user.full_name() + ' is out of office', -// description : 'It works ;)', - }); - }); - - res.send( cal.toString() ); - }) - .catch(function(){ - // TODO VPP set 404 status - res.send('N/A'); - }); + const comments = await getCommentsForLeave({ leave: day.leave_obj }) -}); + cal.createEvent({ + start: start.toDate(), + end: end.toDate(), + allDay, + summary: day.user.full_name() + ' is OOO (out of office)', + description: + comments.length > 0 + ? `With comments: ${comments + .map(({ comment }) => comment) + .join('. ')}` + : '' + }) + } + res.send(cal.toString()) + }) + .catch(error => { + console.log(`Failed to fetch feed because of: ${error}`) + res.status(404).send('N/A') + }) +}) -module.exports = router; +module.exports = router diff --git a/lib/route/integration_api.js b/lib/route/integration_api.js new file mode 100644 index 000000000..eac9e8d86 --- /dev/null +++ b/lib/route/integration_api.js @@ -0,0 +1,127 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const moment = require('moment') +const TeamView = require('../model/team_view') +const { getAudit } = require('../model/audit') +const { getUsersWithLeaves } = require('../model/Report') + +module.exports = passport => { + router.all( + /.*/, + passport.authenticate('bearer', { session: false }), + (req, res, next) => { + if (req.isAuthenticated()) { + return next() + } + + return res.status(401).json({ ok: false }) + } + ) + + router.get('/', (_req, res) => res.json({ ok: true })) + + router.get('/report/allowance', (req, res) => { + const startDate = validator.isDate(req.params.start_date) + ? moment.utc(req.params.start_date) + : req.user.company.get_today() + + const endDate = validator.isDate(req.params.end_date) + ? moment.utc(req.params.end_date) + : req.user.company.get_today() + + const teamView = new TeamView({ + user: req.user, + start_date: startDate, + end_date: endDate + }) + + const currentDeparmentId = validator.isNumeric(req.params.department) + ? req.params.department + : null + + Promise.join( + teamView.promise_team_view_details({ + department_id: currentDeparmentId + }), + req.user.get_company_with_all_leave_types(), + (teamViewDetails, company) => + teamView + .inject_statistics({ + team_view_details: teamViewDetails, + leave_types: company.leave_types + }) + .then(teamViewDetails => + res.json({ + data: [ + teamViewDetails.users_and_leaves.map(ul => ({ + user_id: ul.user.id, + userEmail: ul.user.email, + userLastname: ul.user.lastname, + userName: ul.user.name, + leaveTypeBreakDown: + ul.statistics.leave_type_break_down.pretty_version, + deductedDays: ul.statistics.deducted_days + })) + ] + }) + ) + ).catch(error => { + console.log( + 'An error occured when user ' + + req.user.id + + ' tried to access /reports/allowancebytime page: ' + + error + ) + + res.json({ error }) + }) + }) + + router.get('/report/absence', (req, res) => { + const startDate = + req.params.start_date && validator.isDate(req.params.start_date) + ? moment.utc(req.params.start_date) + : req.user.company.get_today() + + const endDate = + req.params.end_date && validator.isDate(req.params.end_date) + ? moment.utc(req.params.end_date) + : req.user.company.get_today() + + const department_id = + req.params.department && validator.isNumeric(req.params.department) + ? req.params.department + : null + + getUsersWithLeaves({ + company: req.user.company, + startDate, + endDate, + department_id + }) + .then(data => res.json(data)) + .catch(error => { + console.log( + `An error occured when trying to access /report/absence: ${error} at ${ + error.stack + }` + ) + res.json({ error: `${error}` }) + }) + }) + + router.get('/audit/', (req, res) => { + getAudit({ company_id: req.user.company_id }) + .then(data => res.json(data)) + .catch(error => { + console.log(`Failed to fetch Audit data: ${error} at ${error.stack}`) + res.json({ error: `${error}` }) + }) + }) + + return router +} diff --git a/lib/route/login.js b/lib/route/login.js index 614178490..14f9403b8 100644 --- a/lib/route/login.js +++ b/lib/route/login.js @@ -1,4 +1,3 @@ - /* * Contain handlers for dealing with user account: * - login @@ -10,347 +9,515 @@ * not the router itself! * Exported function gets passport object. * */ -'use strict'; +'use strict' + +const validator = require('validator') +const Promise = require('bluebird') +const fs = require('fs') +const config = require('../config') +const moment_tz = require('moment-timezone') +const EmailTransport = require('../email') +const SlackTransport = require('../slack') +const env_var = process.env + +const multer = require('multer') +const upload = multer() -var - validator = require('validator'), - Promise = require('bluebird'), - formidable = require('formidable'), - fs = require("fs"), - config = require('../config'), - EmailTransport = require('../email'); +Promise.promisifyAll(fs) -Promise.promisifyAll(fs); +function get_contact_email_address_for_anonymous_session(req) { + return process.env.CONTACT_EMAIL_ADDRESS || config.get('contact:email') +} -var get_url_to_site_root_for_anonymous_session = function(req) { +function get_url_to_site_root_for_anonymous_session(req) { return req.get('host').indexOf('app.timeoff') < 0 ? '/' - : config.get('promotion_website_domain'); + : config.get('branding:website') } -module.exports = function(passport) { - - var express = require('express'); - var router = express.Router(); +/** + * Determines if registration is allowed based on environment and configuration settings. + * + * @returns {boolean} True if registration is allowed, false otherwise. + */ +function isRegistrationAllowed() { + const envRegOption = process.env.OPTION_ALLOW_NEW_REGISTRATIONS + console.log( + 'Environment variable OPTION_ALLOW_NEW_REGISTRATIONS: ' + envRegOption + ) + + // First priority to the environment variable if it's explicitly set to 'true' or 'false' + if (envRegOption === 'true') { + return true + } else if (envRegOption === 'false') { + return false + } + + // Next, check the application configuration + const configRegOption = config.get('options:registration') + return JSON.parse(configRegOption || 'false') +} - router.get('/login', function(req, res){ - res.render('login', { - allow_create_new_accounts: JSON.parse(config.get('allow_create_new_accounts')), - title : 'Time Off Management', - url_to_the_site_root : get_url_to_site_root_for_anonymous_session(req), - }); - }); +module.exports = function(passport) { + const express = require('express') + const router = express.Router() + + router.get('/login', (req, res) => { + res.render('login', { + login: config.get('login') || { default: true }, + allow_create_new_accounts: isRegistrationAllowed(), + title: 'Login | TimeOff', + url_to_the_site_root: get_url_to_site_root_for_anonymous_session(req), + contact_email_address: get_contact_email_address_for_anonymous_session( + req + ) + }) + }) + + router.get( + '/auth/google', + passport.authenticate('google', { scope: ['email'] }) + ) + + router.get( + '/auth/google/callback', + passport.authenticate('google', { failureRedirect: '/login' }), + (req, res) => { + // Successful authentication, redirect home. + res.redirect('/') + } + ) - router.post('/login', function(req, res, next) { - passport.authenticate('local', function(err, user, info) { - if (err) { return next(err); } + router.post('/login', (req, res, next) => { + passport.authenticate('local', (err, user) => { + if (err) { + return next(err) + } if (!user) { - req.session.flash_error('Incorrect credentials'); - return res.redirect_with_session('/login'); + req.session.flash_error('Incorrect credentials') + return res.redirect_with_session('/login') } - req.logIn(user, function(err) { - if (err) { return next(err); } + req.logIn(user, err => { + if (err) { + return next(err) + } - req.session.flash_message('Welcome back '+user.name+'!'); + req.session.flash_message('Welcome back ' + user.name + '!') - return res.redirect_with_session('/'); - }); - })(req, res, next); - }); + return res.redirect_with_session('/') + }) + })(req, res, next) + }) - router.get('/logout', function(req, res){ + router.get('/logout', (req, res) => { + // Maybe this check is redundant but to be on safe side lets do it + if (!req.user) { + return res.redirect_with_session(303, '/') + } - // Maybe this check is redundant but to be on safe side lets do it - if ( !req.user ) { - return res.redirect_with_session(303, '/'); - } + req.logout() - req.logout(); + return res.redirect_with_session(res.locals.url_to_the_site_root) + }) - return res.redirect_with_session(res.locals.url_to_the_site_root); - }); + router.get('/register', (req, res) => { + // Initialize allow_reg based on the environment variable or configuration setting + const allow_reg = isRegistrationAllowed() - router.get('/register', function(req, res){ + if (!allow_reg) { + return res.redirect_with_session(res.locals.url_to_the_site_root) + } - // Disable new accounts. - if ( !JSON.parse(config.get('allow_create_new_accounts')) ) { - return res.redirect_with_session(res.locals.url_to_the_site_root); - } + console.log('Allowing new registrations: ' + allow_reg) - // There is no need to register new accounts when user alreeady login - if ( req.user ) { - return res.redirect_with_session(303, '/'); - } + // There is no need to register new accounts when user alreeady login + if (req.user) { + return res.redirect_with_session(303, '/') + } - res.render('register',{ - url_to_the_site_root : get_url_to_site_root_for_anonymous_session(req), - countries : config.get('countries'), - }); - }); + // map country codes to country names + const countryNameList = config.get('countries') - router.post('/register', function(req, res){ + if (allow_reg) { + res.render('register', { + url_to_the_site_root: get_url_to_site_root_for_anonymous_session(req), + countries: countryNameList, + timezones_available: moment_tz.tz.names(), + title: 'Register new company | TimeOff' + }) + } + }) - // There is no need to register new accounts when user alreeady login - // (just to prevent people to mess around) - if ( req.user ) { - return res.redirect_with_session(303, '/'); - } + router.post('/register', (req, res) => { + // There is no need to register new accounts when user already login + // (just to prevent people to mess around) + if (req.user) { + return res.redirect_with_session(303, '/') + } - // TODO at some point we need to unified form validation code - // and make it reusable + // TODO at some point we need to unified form validation code + // and make it reusable - var email_validation_re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; + const email = req.body.email + if (!email) { + req.session.flash_error('Email was not provided') + } else if (!validator.isEmail(email)) { + req.session.flash_error('Email address is invalid') + } - var email = req.param('email'); - if (!email){ - req.session.flash_error('Email was not provided'); - } else if ( ! email_validation_re.test( email )) { - req.session.flash_error('Email address is invalid'); - } + const name = req.body.name - var name = req.param('name'); - if (!name){ - req.session.flash_error('Name was not specified'); - } + const slack_username = req.body.slack_username || '' + /* Slack username is not mandatory. + if (!slack_username) { + req.session.flash_error('Slack username was not specified') + } + */ - var lastname = req.param('lastname'); - if (!lastname) { - req.session.flash_error('Last was not specified'); - } + if (!name) { + req.session.flash_error('Name was not specified') + } - var company_name = req.param('company_name'); + const lastname = req.body.lastname + if (!lastname) { + req.session.flash_error('Last name was not specified') + } - var password = req.param('password'); - if (!password) { - req.session.flash_error('Password could not be blank'); - } else if ( password !== req.param('password_confirmed') ) { - req.session.flash_error('Confirmed password does not match initial one'); - } + const company_name = req.body.company_name - var country_code = req.param('country'); - if (! validator.matches(country_code, /^[a-z]{2}/i) ){ - req.session.flash_error('Incorrect country code'); - } + const password = req.body.password + if (!password) { + req.session.flash_error('Password could not be blank') + } else if (password !== req.body.password_confirmed) { + req.session.flash_error('Confirmed password does not match initial one') + } - // In case of validation error redirect back to registration form - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('/register/'); - } + const country_code = req.body.country + if (!validator.matches(country_code, /^[a-z]{2}/i)) { + req.session.flash_error('Incorrect country code') + } + + const timezone = validator.trim(req.body.timezone) + if (!moment_tz.tz.names().find(tz_str => tz_str === timezone)) { + req.session.flash_error('Time zone is unknown') + } - // Try to create new record of user - req.app.get('db_model').User.register_new_admin_user({ - email : email.toLowerCase(), - password : password, - name : name, - lastname : lastname, - company_name : company_name, - country_code : country_code, + // In case of validation error redirect back to registration form + if (req.session.flash_has_errors()) { + return res.redirect_with_session('/register/') + } + + // Try to create new record of user + console.log( + 'Registering new user', + email, + slack_username, + name, + lastname, + company_name, + country_code, + timezone + ) + req.app + .get('db_model') + .User.register_new_admin_user({ + email: email.toLowerCase(), + slack_username: slack_username.toLowerCase(), + password, + name, + lastname, + company_name, + country_code, + timezone }) // Send registration email - .then(function(user){ - var email = new EmailTransport(); - - return email.promise_registration_email({ - user : user, - }) - .then(function(){ - return Promise.resolve(user) - }); + .then(user => { + console.log('Sending registration email to ' + user.email) + + const email = new EmailTransport() + + return ( + email + .promise_registration_email({ + user + }) + .then(() => Promise.resolve(user)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send registration email to ' + + user.email + + ' : ' + + error, + error.stack + ) + return Promise.resolve(user) + }) + ) + }) + .then(user => { + console.log('Sending Slack notification to ' + user.email) + + const Slack = new SlackTransport() + + return ( + Slack.promise_registration_slack({ + user + }) + .then(() => Promise.resolve(user)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send slack notification to ' + + user.email + + ' : ' + + error, + error.stack + ) + return Promise.resolve(user) + }) + ) }) - .then(function(user){ + .then(user => { + console.log('Authenticated the newly created user ' + user.email) // Login newly created user - req.logIn(user, function(err) { - if (err) { return next(err); } - - req.session.flash_message( - 'Registration is complete.' - ); + req.logIn(user, err => { + if (err) { + console.error(err) + return + } - return res.redirect_with_session('/'); - }); + req.session.flash_message('Registration is complete.') + return res.redirect_with_session('/') + }) }) - .catch(function(error){ - console.error( - 'An error occurred when trying to register new user ' - + email + ' : ' + error - ); - - req.session.flash_error( - 'Failed to register user please contact customer service.'+ - (error.show_to_user ? ' '+ error : '') - ); - - return res.redirect_with_session('/register/'); - }); - - }); - - router.get('/forgot-password/', function(req, res){ - - res.render('forgot_password',{ - url_to_the_site_root : get_url_to_site_root_for_anonymous_session(req), - }); - }); - - router.post('/forgot-password/', function(req, res){ - var email = req.param('email'); - - if (!email){ - req.session.flash_error('Email was not provided'); - - } else if ( ! validator.isEmail(email)) { - req.session.flash_error('Email address is invalid'); + .catch(error => { + console.error( + 'An error occurred when trying to register new user ' + + email + + ' : ' + + error, + error.stack + ) + + req.session.flash_error( + 'Failed to register user please contact customer service.' + + (error.show_to_user ? ' ' + error : '') + ) + + return res.redirect_with_session('/register/') + }) + }) + + router.get('/forgot-password/', (req, res) => { + res.render('forgot_password', { + url_to_the_site_root: get_url_to_site_root_for_anonymous_session(req), + title: 'Forgotten password | TimeOff' + }) + }) + + router.post('/forgot-password/', (req, res) => { + let email = req.body.email + + if (!email) { + req.session.flash_error('Email was not provided') + } else if (!validator.isEmail(email)) { + req.session.flash_error('Email address is invalid') } // In case of validation error redirect back to forgot password form - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('./'); + if (req.session.flash_has_errors()) { + return res.redirect_with_session('./') } - var success_msg ='Please check your email box for further instructions'; + const success_msg = 'Please check your email box for further instructions' // Normalize email address: system operates only in low cased emails - email = email.toLowerCase(); - - req.app.get('db_model').User.find_by_email(email) - .then(function(user){ + email = email.toLowerCase() + req.app + .get('db_model') + .User.find_by_email(email) + .then(user => { if (!user) { - req.session.flash_message(success_msg); + req.session.flash_message(success_msg) - var error = new Error(''); - error.do_not_report = true; - throw error; + const error = new Error('') + error.do_not_report = true + throw error } - return Promise.resolve(user); + return Promise.resolve(user) }) - .then(function(user){ - var Email = new EmailTransport(); + .then(user => { + const Slack = new SlackTransport() + + Slack.promise_forgot_password_slack({ + user + }) + + const Email = new EmailTransport() return Email.promise_forgot_password_email({ - user : user, - }); + user + }) }) - .then(function(){ - req.session.flash_message(success_msg); - return res.redirect_with_session('./'); + .then(() => { + req.session.flash_message(success_msg) + return res.redirect_with_session('./') }) - .catch(function(error){ - - if (error.do_not_report ){ - return res.redirect_with_session('./'); + .catch(error => { + if (error.do_not_report) { + return res.redirect_with_session('./') } - console.error('An error occurred while submittin forgot password form: '+error); - req.session.flash_error('Failed to proceed with submitted data.'); - return res.redirect_with_session('./'); - }); - - }); - - router.get('/reset-password/', function(req, res){ + console.error( + 'An error occurred while submittin forgot password form: ' + error, + error.stack + ) + req.session.flash_error('Failed to proceed with submitted data.') + return res.redirect_with_session('./') + }) + }) - var token = req.param('t'); + router.get('/reset-password/', (req, res) => { + const token = req.query.t - req.app.get('db_model').User.get_user_by_reset_password_token(token) - .then(function(user){ - if (! user) { - req.session.flash_error('Unknown reset password link, please submit request again'); + req.app + .get('db_model') + .User.get_user_by_reset_password_token(token) + .then(user => { + if (!user) { + req.session.flash_error( + 'Unknown reset password link, please submit request again' + ) return res.redirect_with_session('/forgot-password/') } - res.render('reset_password',{ - url_to_the_site_root : get_url_to_site_root_for_anonymous_session(req), - token : token, - }); - }); - }); - - router.post('/reset-password/', function(req, res){ + res.render('reset_password', { + url_to_the_site_root: get_url_to_site_root_for_anonymous_session(req), + token, + title: 'Reset password | TimeOff' + }) + }) + }) - var token = req.param('t'), - password = req.param('password'), - confirm_password = req.param('confirm_password'); + // adding criteria for password complexity + const PASSWORD_MIN_LENGTH = 8 + const PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ + router.post('/reset-password/', (req, res) => { + const token = req.body.t + const password = req.body.password + const confirm_password = req.body.confirm_password + // check if match if (password !== confirm_password) { - req.session.flash_error('Confirmed password does not match password'); - return res.redirect_with_session('/reset-password/?t='+token); + req.session.flash_error('Confirmed password does not match password') + return res.redirect_with_session('/reset-password/?t=' + token) + } + + // check pw length + if (password.length < PASSWORD_MIN_LENGTH) { + req.session.flash_error( + 'Password must be at least ' + PASSWORD_MIN_LENGTH + ' characters long' + ) + return res.redirect_with_session('/reset-password/?t=' + token) } - req.app.get('db_model').User.get_user_by_reset_password_token(token) - .then(function(user){ - if (! user) { - req.session.flash_error('Unknown reset password link, please submit request again'); - return res.redirect_with_session('/forgot-password/'); + // check pw complexity + if (!validator.matches(password, PASSWORD_REGEX)) { + console.log('Password validation failed') + req.session.flash_error( + 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character' + ) + return res.redirect_with_session('/reset-password/?t=' + token) + } + + req.app + .get('db_model') + .User.get_user_by_reset_password_token(token) + .then(user => { + if (!user) { + req.session.flash_error( + 'Unknown reset password link, please submit request again' + ) + return res.redirect_with_session('/forgot-password/') } - return Promise.resolve(user); - }) - .then(function(user){ - user.password = req.app.get('db_model').User.hashify_password(password); - return user.save(); + return Promise.resolve(user) }) - .then(function(user){ - var Email = new EmailTransport(); - - return Email.promise_reset_password_email({ - user : user, - }); + .then(user => { + user.password = req.app.get('db_model').User.hashify_password(password) + return user.save() }) - .then(function(){ - req.session.flash_message('Please use new password to login into system'); - return res.redirect_with_session('/login/') - }); - }); + .then(user => { + const Slack = new SlackTransport() - router.post('/import-company/', function(req, res){ + Slack.promise_reset_password_slack({ + user + }) - var form = new formidable.IncomingForm(), - parseAsync = Promise.promisify(form.parse); + const Email = new EmailTransport() - parseAsync.call(form, req) - .then(function(args){ + return Email.promise_reset_password_email({ + user + }) + }) + .then(() => { + req.session.flash_message( + 'Please use new password to login into system' + ) + return res.redirect_with_session('/login/') + }) + }) - var files = args[1]; + router.post('/import-company/', upload.single('company_dump'), (req, res) => + Promise.resolve() + .then(() => { + console.log('req.file', req.file) - if (files.company_dump.size === 0) { - throw new Error('No dump file to restore from was provided'); + if (req.file.size === 0) { + throw new Error('No dump file to restore from was provided') } - return fs.readFileAsync(files.company_dump.path, "utf8"); - }) - .then(function(dump_json){ + if (req.file.path && !req.file.buffer) { + // disk storage + return fs.readFileAsync(req.file.path, 'utf8') + } - return Promise.resolve(JSON.parse(dump_json)); + // memory storage + return req.file.buffer.toString() }) - .then(function(raw_company_obj){ - - return req.app.get('db_model').Company.restore_from_dump({ - dump_json : raw_company_obj, - }); + .then(dump_json => Promise.resolve(JSON.parse(dump_json))) + .then(raw_company_obj => + req.app.get('db_model').Company.restore_from_dump({ + dump_json: raw_company_obj + }) + ) + .then(company => { + req.session.flash_message('Company ' + company.name + ' was restored') + res.redirect_with_session('/import-company/') }) - .then(function(company){ - req.session.flash_message('Company '+ company.name +' was restored'); - res.redirect_with_session('/import-company/'); + .catch(error => { + req.session.flash_error( + 'Failed to import company, due to error: ' + error, + error.stack + ) + res.redirect_with_session('/import-company/') }) - .catch(function(error){ - req.session.flash_error('Failed to import company, due to error: '+error); - res.redirect_with_session('/import-company/'); - }); + ) - }); - - router.get('/import-company/', function(req, res){ + router.get('/import-company/', (req, res) => { res.render('import_company', { - url_to_the_site_root : get_url_to_site_root_for_anonymous_session(req), - }); - }); + url_to_the_site_root: get_url_to_site_root_for_anonymous_session(req), + title: 'Import company | TimeOff' + }) + }) - return router; -}; + return router +} diff --git a/lib/route/reports.js b/lib/route/reports.js new file mode 100644 index 000000000..d5858c9c1 --- /dev/null +++ b/lib/route/reports.js @@ -0,0 +1,387 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const moment = require('moment') +const config = require('../config') +const TeamView = require('../model/team_view') +const Exception = require('../error') +const csv = Promise.promisifyAll(require('csv')) +const _ = require('underscore') + +const { fetchLeavesForLeavesReport } = require('../model/Report') +const { sorter } = require('../util') + +// Make sure that current user is authorized to deal with settings +router.all(/.*/, require('../middleware/ensure_user_is_admin')) + +router.get('/', (_req, res) => { + res.render('report/index', { + title: 'Reports | TimeOff' + }) +}) + +router.get('/allowancebytime/', (req, res) => { + const start_date = + req.query.start_date && validator.toDate(req.query.start_date) + ? moment.utc(req.query.start_date) + : req.user.company.get_today() + + const end_date = + req.query.end_date && validator.toDate(req.query.end_date) + ? moment.utc(req.query.end_date) + : req.user.company.get_today() + + const team_view = new TeamView({ + user: req.user, + start_date, + end_date + }) + + const current_deparment_id = + typeof req.query.department === 'number' || + (req.query.department && validator.isNumeric(req.query.department)) + ? req.query.department + : null + + Promise.join( + team_view.promise_team_view_details({ + department_id: current_deparment_id + }), + req.user.get_company_with_all_leave_types(), + (team_view_details, company) => + team_view + .inject_statistics({ + team_view_details, + leave_types: company.leave_types + }) + .then(team_view_details => + render_allowancebytime({ + params: req.query, + session: req.session, + res, + team_view_details, + company, + start_date, + end_date + }) + ) + ).catch(error => { + console.error( + 'An error occured when user ' + + req.user.id + + ' tried to access /reports/allowancebytime page: ', + error, + error.stack + ) + + let user_error_message = + 'Failed to produce report. Please contact administrator.' + // By default go back to root report page + let redirect_path = '../' + + if (error.tom_error) { + user_error_message = Exception.extract_user_error_message(error) + + // If it is known error: stay on current page + redirect_path = './' + } + + req.session.flash_error(user_error_message) + + return res.redirect_with_session(redirect_path) + }) +}) + +function render_allowancebytime(args) { + const params = args.params + const session = args.session + const res = args.res + const team_view_details = args.team_view_details + const company = args.company + const start_date = args.start_date + const end_date = args.end_date + + return Promise.try(() => + params['as-csv'] + ? render_allowancebytime_as_csv(args) + : res.render('report/allowancebytime', { + users_and_leaves: team_view_details.users_and_leaves, + related_departments: team_view_details.related_departments, + current_department: team_view_details.current_department, + company, + start_date_str: start_date.format('YYYY-MM'), + end_date_str: end_date.format('YYYY-MM'), + start_date_obj: start_date, + end_date_obj: end_date, + same_month: start_date.format('YYYYMM') === end_date.format('YYYYMM'), + title: 'Reports - Allowance by time | TimeOff' + }) + ) +} + +function render_allowancebytime_as_csv(args) { + const params = args.params + const session = args.session + const res = args.res + const team_view_details = args.team_view_details + const company = args.company + const start_date = args.start_date + const end_date = args.end_date + + // Compose file name + res.attachment( + company.name_for_machine() + + '_employee_allowances_between' + + start_date.format('YYYY_MM') + + '_and_' + + end_date.format('YYYY_MM') + + '.csv' + ) + + // Compose result CSV header + const content = [ + ['email', 'last name', 'name'] + // Add dynamic list of Leave Types + .concat( + team_view_details.users_and_leaves.length > 0 + ? team_view_details.users_and_leaves[0].statistics.leave_type_break_down.pretty_version.map( + it => it.name + ) + : [] + ) + .concat(['days deducted from allowance']) + ] + + // ... and body + team_view_details.users_and_leaves.forEach(ul => { + content.push( + [ul.user.email, ul.user.lastname, ul.user.name] + // Dynamic part of the column list + .concat( + ul.statistics.leave_type_break_down.pretty_version.map(it => it.stat) + ) + .concat([ul.statistics.deducted_days]) + ) + }) + + return csv + .stringifyAsync(content) + .then(csv_data_string => res.send(csv_data_string)) +} + +const extractParametersForLeavesReport = ({ req, actingUser }) => { + const { start_date, end_date, department, leave_type } = req.query + + const startDate = + start_date && validator.isDate(start_date) + ? moment.utc(start_date) + : moment.utc(actingUser.company.get_today()).startOf('month') + + const endDate = + end_date && validator.isDate(end_date) + ? moment.utc(end_date) + : moment.utc(actingUser.company.get_today()).endOf('month') + + const departmentId = + department && validator.isNumeric(department.toString()) ? department : null + + const leaveTypeId = + leave_type && validator.isNumeric(leave_type.toString()) ? leave_type : null + + return { startDate, endDate, departmentId, leaveTypeId } +} + +const renderLeavesReportAsCsv = async ({ + res, + company, + startDate, + endDate, + leaves +}) => { + // Compose file name + res.attachment( + `${company.name_for_machine()}_leaves_report_between_${startDate.format( + 'YYYY_MM_DD' + )}_and_${endDate.format('YYYY_MM_DD')}.csv` + ) + + // Compose result CSV header + const content = [ + [ + 'Employee', + 'Department', + 'Leave Type', + 'Deducted days', + 'From', + 'To', + 'Status', + 'Requested On', + 'Approved By', + 'Requestor Comment', + 'Approver Comment' + ] + ] + + // ... and body + content.push( + ...leaves.map( + ({ + employeeFullName, + departmentName, + type, + deductedDays, + startDate, + endDate, + status, + createdAt, + approver, + employee_comment, + approver_comment + }) => [ + employeeFullName, + departmentName, + type, + deductedDays, + startDate, + endDate, + status, + createdAt, + approver, + employee_comment, + approver_comment + ] + ) + ) + + const csvString = await csv.stringifyAsync(content) + + return res.send(csvString) +} + +const defaultSortAttributeForLeaveReport = 'employeeFullName' +const sortersForLeavesReport = { + employeeFullName: (a, b) => sorter(a.employeeLastName, b.employeeLastName), + departmentName: (a, b) => sorter(a.departmentName, b.departmentName), + type: (a, b) => sorter(a.type, b.type), + startDate: (a, b) => + moment + .utc(a.startDate) + .toDate() + .valueOf() - + moment + .utc(b.startDate) + .toDate() + .valueOf(), + endDate: (a, b) => + moment + .utc(a.endDate) + .toDate() + .valueOf() - + moment + .utc(b.endDate) + .toDate() + .valueOf(), + status: (a, b) => sorter(a.status, b.status), + createdAt: (a, b) => + moment + .utc(a.createdAt) + .toDate() + .valueOf() - + moment + .utc(b.createdAt) + .toDate() + .valueOf(), + approver: (a, b) => sorter(a.approver, b.approver) +} + +const getSorterForLeaves = (attribute = defaultSortAttributeForLeaveReport) => + sortersForLeavesReport[attribute] || + sortersForLeavesReport[defaultSortAttributeForLeaveReport] + +router.get('/leaves/', async (req, res) => { + const actingUser = req.user + const dbModel = req.app.get('db_model') + const renderAsCsv = !!req.query['as-csv'] + const sortBy = req.query.sort_by || defaultSortAttributeForLeaveReport + let leaves = [] + + const { + startDate, + endDate, + departmentId, + leaveTypeId + } = extractParametersForLeavesReport({ req, actingUser }) + + try { + const res = await fetchLeavesForLeavesReport({ + actingUser, + dbModel, + startDate, + endDate, + departmentId, + leaveTypeId + }) + + leaves = res.leaves.sort(getSorterForLeaves(sortBy)) + } catch (error) { + console.error( + `An error occurred when user ${ + actingUser.id + } tried to access /reports/leaves/ page: ${error} at ${error.stack}` + ) + + let userErrorMessage = + 'Failed to produce Leaves report. Please contact administrator.' + + // By default go back to root report page + let redirectPath = '../' + + if (error.tom_error) { + userErrorMessage = Exception.extract_user_error_message(error) + + // If it is known error: stay on current page + redirectPath = './' + } + + req.session.flash_error(userErrorMessage) + + return res.redirect_with_session(redirectPath) + } + + const company = await actingUser.getCompany({ + scope: ['with_leave_types', 'with_simple_departments'] + }) + + if (renderAsCsv) { + await renderLeavesReportAsCsv({ res, company, startDate, endDate, leaves }) + } else { + res.render('report/leaves', { + leaves, + departmentId, + leaveTypeId, + sortBy, + startDateObj: startDate, + endDateObj: endDate, + startDateStr: startDate.format('YYYY-MM-DD'), + endDateStr: endDate.format('YYYY-MM-DD'), + company: actingUser.company, + leaveTypes: company.leave_types + ? company.leave_types + .map(lt => lt.toJSON()) + .map(lt => ({ ...lt, id: `${lt.id}` })) + .sort((a, b) => sorter(a.name, b.name)) + : [], + departments: company.departments + ? company.departments + .map(d => d.toJSON()) + .map(d => ({ ...d, id: `${d.id}` })) + .sort((a, b) => sorter(a.name, b.name)) + : [] + }) + } +}) + +module.exports = router diff --git a/lib/route/requests.js b/lib/route/requests.js index 390153b18..097d984cb 100644 --- a/lib/route/requests.js +++ b/lib/route/requests.js @@ -1,277 +1,399 @@ - -"use strict"; - -var express = require('express'), - router = express.Router(), - Promise = require('bluebird'), - moment = require('moment'), - validator = require('validator'), - _ = require('underscore'), - EmailTransport = require('../email'); - -router.get('/', function(req, res){ - - Promise.join( - req.user.promise_my_active_leaves_ever(), - req.user.promise_leaves_to_be_processed(), - function(my_leaves, to_be_approved_leaves){ - - res.render('requests',{ - my_leaves : my_leaves, - to_be_approved_leaves : to_be_approved_leaves, - }); - } - ); -}); +'use strict' + +const express = require('express') +const router = express.Router() +const Promise = require('bluebird') +const validator = require('validator') +const _ = require('underscore') +const LeaveCollectionUtil = require('../model/leave_collection')() +const EmailTransport = require('../email') +const SlackTransport = require('../slack') + +router.get('/', (req, res) => { + const dbModel = req.app.get('db_model') + + Promise.join( + req.user + .promise_my_active_leaves_ever() + .then(leaves => + LeaveCollectionUtil.enrichLeavesWithComments({ leaves, dbModel }) + ) + .then(leaves => LeaveCollectionUtil.promise_to_group_leaves(leaves)), + req.user + .promise_leaves_to_be_processed() + .then(leaves => + LeaveCollectionUtil.enrichLeavesWithComments({ leaves, dbModel }) + ), + (my_leaves_grouped, to_be_approved_leaves) => { + res.render('requests', { + my_leaves_grouped, + to_be_approved_leaves, + title: 'Requests | TimeOff' + }) + } + ) +}) function leave_request_action(args) { - var - current_action = args.action, - leave_action_method = args.leave_action_method, - was_pended_revoke = false; - - return function(req, res){ - - var request_id = validator.trim( req.param('request') ); - - if (!validator.isNumeric(request_id)){ - req.session.flash_error('Failed to ' + current_action); + const current_action = args.action + const leave_action_method = args.leave_action_method + let was_pended_revoke = false + + return function(req, res) { + console.log('req body', req.body) + const request_id = validator.trim(req.body.request) + const comment = req.body.comment ? validator.trim(req.body.comment) : '' + + if ( + typeof request_id !== 'number' && + (!request_id || !validator.isNumeric(request_id)) + ) { + req.session.flash_error('Failed to ' + current_action) } - if ( req.session.flash_has_errors() ) { - console.error('Got validation errors on '+current_action+' request handler'); + if (req.session.flash_has_errors()) { + console.error( + 'Got validation errors on ' + current_action + ' request handler' + ) - return res.redirect_with_session('../'); + return res.redirect_with_session('../') } - Promise.try(function(){ - return req.user.promise_leaves_to_be_processed(); - }) - .then(function(leaves){ - var leave_to_process = _.find(leaves, function(leave){ - return String(leave.id) === String(request_id) - && (leave.is_new_leave() || leave.is_pended_revoke_leave()); - }); - - if (! leave_to_process) { - throw new Error('Provided ID '+request_id - +'does not correspond to any leave requests to be '+current_action - +'ed for user ' + req.user.id - ); - } - - was_pended_revoke = leave_to_process.is_pended_revoke_leave(); - - return leave_to_process[leave_action_method](); - }) - .then(function(processed_leave){ - return processed_leave.reload({ - include : [ - {model : req.app.get('db_model').User, as : 'user'}, - {model : req.app.get('db_model').User, as : 'approver'}, - {model : req.app.get('db_model').LeaveType, as : 'leave_type' }, - ], - }); - }) - .then(function(processed_leave){ + Promise.try(() => req.user.promise_leaves_to_be_processed()) + .then(leaves => { + const leave_to_process = _.find( + leaves, + leave => + String(leave.id) === String(request_id) && + (leave.is_new_leave() || leave.is_pended_revoke_leave()) + ) - var Email = new EmailTransport(); + if (!leave_to_process) { + throw new Error( + 'Provided ID ' + + request_id + + 'does not correspond to any leave requests to be ' + + current_action + + 'ed for user ' + + req.user.id + ) + } - return Email.promise_leave_request_decision_emails({ - leave : processed_leave, - action : current_action, - was_pended_revoke : was_pended_revoke, - }) - .then(function(){ - return Promise.resolve( processed_leave); - }); - }) - .then(function(processed_leave){ - req.session.flash_message('Request from '+processed_leave.user.full_name() - +' was '+current_action+'ed'); + was_pended_revoke = leave_to_process.is_pended_revoke_leave() - return res.redirect_with_session('../'); - }) - .catch(function(error){ - console.error('An error occurred when attempting to '+current_action - +' leave request '+request_id+' by user '+req.user.id+' Error: '+error - ); - req.session.flash_error('Failed to '+current_action); - return res.redirect_with_session('../'); - }); - }; + return leave_to_process[leave_action_method]({ by_user: req.user }) + }) + .then(processed_leave => { + // save comment to db (kinda hacky, but oh well) + processed_leave.approver_comment = comment // adding directly + return processed_leave.save() // multiple saves + }) + .then(processed_leave => + processed_leave.reload({ + include: [ + { model: req.app.get('db_model').User, as: 'user' }, + { model: req.app.get('db_model').User, as: 'approver' }, + { model: req.app.get('db_model').LeaveType, as: 'leave_type' } + ] + }) + ) + .then(processed_leave => { + const Email = new EmailTransport() + + return ( + Email.promise_leave_request_decision_emails({ + leave: processed_leave, + action: current_action, + was_pended_revoke + }) + .then(() => Promise.resolve(processed_leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send email for the leave request: ' + error, + error.stack + ) + return Promise.resolve(processed_leave) + }) + ) + }) + .then(processed_leave => { + const Slack = new SlackTransport() + + return ( + Slack.promise_leave_request_decision_slacks({ + leave: processed_leave, + action: current_action, + was_pended_revoke + }) + .then(() => Promise.resolve(processed_leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send slack notification for the leave request: ' + + error, + error.stack + ) + return Promise.resolve(processed_leave) + }) + ) + }) + .then(processed_leave => { + req.session.flash_message( + 'Request from ' + processed_leave.user.full_name() + ' was processed' + ) -}; + return res.redirect_with_session('../') + }) + .catch(error => { + console.error( + 'An error occurred when attempting to ' + + current_action + + ' leave request ' + + request_id + + ' by user ' + + req.user.id + + ' Error: ' + + error, + error.stack + ) + req.session.flash_error('Failed to ' + current_action) + return res.redirect_with_session('../') + }) + } +} router.post( '/reject/', leave_request_action({ - action : 'reject', - leave_action_method : 'promise_to_reject', + action: 'reject', + leave_action_method: 'promise_to_reject' }) -); +) router.post( '/approve/', leave_request_action({ - action : 'approve', - leave_action_method : 'promise_to_approve', + action: 'approve', + leave_action_method: 'promise_to_approve' }) -); +) -router.post('/cancel/', function(req, res){ +router.post('/cancel/', (req, res) => { + const request_id = validator.trim(req.body.request) - var request_id = validator.trim( req.param('request') ); + Promise.try(() => req.user.promise_cancelable_leaves()) + .then(leaves => { + const leave_to_cancel = _.find( + leaves, + leave => String(leave.id) === String(request_id) + ) - Promise.try(function(){ - return req.user.promise_cancelable_leaves() - }) - .then(function(leaves){ - var leave_to_cancel = _.find(leaves, function(leave){ - return String(leave.id) === String(request_id); - }); + if (!leave_to_cancel) { + throw new Error( + 'Given leave request is not amoung those current user can cancel' + ) + } - if ( ! leave_to_cancel ) { - throw new Error('Given leave request is not amoung those current user can cancel'); - } + return Promise.resolve(leave_to_cancel) + }) + .then(leave => leave.promise_to_cancel().then(() => Promise.resolve(leave))) + .then(leave => + leave.reload({ + include: [ + { model: req.app.get('db_model').User, as: 'user' }, + { model: req.app.get('db_model').User, as: 'approver' }, + { model: req.app.get('db_model').LeaveType, as: 'leave_type' } + ] + }) + ) + .then(leave => { + const Email = new EmailTransport() + + return ( + Email.promise_leave_request_cancel_emails({ + leave + }) + .then(() => Promise.resolve(leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send email for the leave request: ' + error, + error.stack + ) + return Promise.resolve(leave) + }) + ) + }) + .then(leave => { + const Slack = new SlackTransport() + + return ( + Slack.promise_leave_request_cancel_slacks({ + leave + }) + .then(() => Promise.resolve(leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send slack notification for the leave request: ' + + error, + error.stack + ) + return Promise.resolve(leave) + }) + ) + }) + .then(leave => { + req.session.flash_message('The leave request was canceled') + }) + .catch(error => { + console.log('An error occurred: ' + error, error.stack) + req.session.flash_error('Failed to cancel leave request') + }) + .finally(() => res.redirect_with_session('/requests/')) +}) - return Promise.resolve(leave_to_cancel); - }) - .then(function(leave){ - return leave.promise_to_cancel() - .then(function(){ return Promise.resolve(leave)}); - }) - .then(function(leave){ - return leave.reload({ - include : [ - {model : req.app.get('db_model').User, as : 'user'}, - {model : req.app.get('db_model').User, as : 'approver'}, - {model : req.app.get('db_model').LeaveType, as : 'leave_type' }, - ], - }); - }) - .then(function(leave){ +router.post('/revoke/', (req, res) => { + const request_id = validator.trim(req.body.request) - var Email = new EmailTransport(); + // TODO NOTE revoke action now could be made from more then one place, + // so make sure that user is redirected to correct place - return Email.promise_leave_request_cancel_emails({ - leave : leave, - }) - .then(function(){ - return Promise.resolve(leave); - }); - }) - .then(function(leave){ - req.session.flash_message('The leave request was canceled'); - }) - .catch(function(error){ - console.log('An error occurred: '+error); - req.session.flash_error('Failed to cancel leave request'); - }) - .finally(function(){ - return res.redirect_with_session('/requests/'); - }); -}); + if ( + typeof request_id !== 'number' && + (!request_id || !validator.isNumeric(request_id)) + ) { + req.session.flash_error('Failed to revoke leave request') + } -router.post( - '/revoke/', - function(req, res){ - var request_id = validator.trim( req.param('request') ); + if (req.session.flash_has_errors()) { + console.log( + 'Got validation errors when revoking leave request for user ' + + req.user.id + ) - // TODO NOTE revoke action now could be made from more then one place, - // so make sure that user is redirected to correct place + return res.redirect_with_session('../') + } - if (!validator.isNumeric(request_id)){ - req.session.flash_error('Failed to revoke leave request'); - } + Promise + // Get the Leave object for submitted ID + .try(() => + req.app.get('db_model').Leave.findOne({ where: { id: request_id } }) + ) + + // Ensure that current user can act on this Leave object + .then(requested_leave => { + // Case when requested Leave is originated from current user + if (String(requested_leave.user_id) === String(req.user.id)) { + return Promise.resolve(requested_leave) + } + + // Case when requested Leave is originated from one of employees + // current user can manage + return req.user.promise_users_I_can_manage().then(users => { + if (users.find(u => String(u.id) === String(requested_leave.user_id))) { + return Promise.resolve(requested_leave) + } - if ( req.session.flash_has_errors() ) { - console.error( - 'Got validation errors when revoking leave request for user ' + req.user.id - ); + return Promise.resolve() + }) + }) - return res.redirect_with_session('../'); - } + .then(leave_to_process => { + // Ensure that Leave is in status from it could be revoked + if (!leave_to_process) { + throw new Error( + 'Provided ID ' + + request_id + + ' does not correspond to any leave requests to be revoked by user ' + + req.user.id + ) + } - Promise.try(function(){ - return Promise.join( - // Get user's leaves - req.user.promise_my_active_leaves_ever(), - // Get all leaves from users supervised by current user - req.user - .promise_users_I_can_manage() - .then(function(users){ - return Promise.all( - _.map( - users, - function(user){ - return user.promise_my_active_leaves_ever(); - } - )) - .then(function(array_of_leave_arrays){ - return Promise.resolve(_.flatten(array_of_leave_arrays, true)); - }); - }), - function(users_leaves, supervised_leaves){ - // Compose all leaves into one new array - return Promise.resolve(users_leaves.concat(supervised_leaves)); - }); + // Do the action + return leave_to_process.promise_to_revoke() }) - .then(function(leaves){ - var leave_to_process = _.find(leaves, function(leave){ - return String(leave.id) === String(request_id) - && leave.is_approved_leave(); - }); - - if (! leave_to_process) { - throw new Error('Provided ID '+request_id - +' does not correspond to any leave requests to be revoked by user ' - + req.user.id - ); - } - - return leave_to_process.promise_to_revoke(); + + // Ensure that Leave object has all content necessary for sending emails + .then(processed_leave => + processed_leave.reload({ + include: [ + { model: req.app.get('db_model').User, as: 'user' }, + { model: req.app.get('db_model').User, as: 'approver' }, + { model: req.app.get('db_model').LeaveType, as: 'leave_type' } + ] + }) + ) + + // Send relevant emails + .then(processed_leave => { + const Email = new EmailTransport() + + return ( + Email.promise_leave_request_revoke_emails({ + leave: processed_leave + }) + .then(() => Promise.resolve(processed_leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send email for the leave request: ' + error, + error.stack + ) + return Promise.resolve(processed_leave) + }) + ) }) - .then(function(processed_leave){ - return processed_leave.reload({ - include : [ - {model : req.app.get('db_model').User, as : 'user'}, - {model : req.app.get('db_model').User, as : 'approver'}, - {model : req.app.get('db_model').LeaveType, as : 'leave_type' }, - ], - }); + + // Send relevant slacks + .then(processed_leave => { + console.log('Processing SLACK') + const Slack = new SlackTransport() + + return ( + Slack.promise_leave_request_revoke_slacks({ + leave: processed_leave + }) + .then(() => Promise.resolve(processed_leave)) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send slack notification for the leave request: ' + + error, + error.stack + ) + return Promise.resolve(processed_leave) + }) + ) }) - .then(function(processed_leave){ - var Email = new EmailTransport(); + // Deal with next page: where to land and what to show + .then(processed_leave => { + req.session.flash_message( + 'You have requested leave to be revoked. ' + + (processed_leave.is_auto_approve() + ? '' + : 'Your supervisor needs to approve it') + ) - return Email.promise_leave_request_revoke_emails({ - leave : processed_leave, - }) - .then(function(){ - return Promise.resolve(processed_leave); - }); + return res.redirect_with_session('../') }) - .then(function(processed_leave){ - req.session.flash_message( - 'You have requested leave to be revoked. ' - + ( - processed_leave.user.is_auto_approve() - ? '' - : 'Your supervisor needs to approve it' - ) - ); - return res.redirect_with_session('../'); + // Deal with issues if any occurs + .catch(error => { + console.error( + 'An error occurred when attempting to revoke leave request ' + + request_id + + ' by user ' + + req.user.id + + ' Error: ', + error, + error.stack + ) + req.session.flash_error('Failed to revoke leave request') + return res.redirect_with_session('../') }) - .catch(function(error){ - console.error('An error occurred when attempting to revoke leave request ' - +request_id+' by user '+req.user.id+' Error: '+error - ); - req.session.flash_error('Failed to revoke leave request'); - return res.redirect_with_session('../'); - }); - } -); +}) -module.exports = router; +module.exports = router diff --git a/lib/route/settings.js b/lib/route/settings.js index 8301a82ee..0aa8fb056 100644 --- a/lib/route/settings.js +++ b/lib/route/settings.js @@ -2,925 +2,797 @@ * * */ -"use strict"; - -var express = require('express'), - router = express.Router(), - validator = require('validator'), - Promise = require('bluebird'), - moment = require('moment'), - config = require('../config'), - _ = require('underscore'); +'use strict' + +const express = require('express') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const moment = require('moment') +const moment_tz = require('moment-timezone') +const config = require('../config') +const Exception = require('../error') +const { extractUserErrorMessage, extractSystemErrorMessage } = Exception +const CompanyRemover = require('../model/company/remover') +const { + calculateCarryOverAllowance +} = require('../model/calculateCarryOverAllowance') +const uuidv4 = require('uuid/v4') +const _ = require('underscore') + +const CompanyExporter = require('../model/company/exporter') +const { sorter } = require('../util') // Make sure that current user is authorized to deal with settings -router.all(/.*/, require('../middleware/ensure_user_is_admin')); - - -router.get('/general/', function(req, res){ - - res.locals.custom_java_script.push( - '/js/settings_general.js' - ); - - res.locals.custom_css.push( - '/css/bootstrap-datepicker3.standalone.css' - ); +router.all(/.*/, require('../middleware/ensure_user_is_admin')) + +router.get('/general/', async (req, res) => { + try { + res.locals.custom_java_script.push('/js/settings_general.js') + res.locals.custom_css.push('/css/bootstrap-datepicker3.standalone.css') + + const model = req.app.get('db_model') + + // Fetch the company with necessary associations + const company = await req.user.getCompany({ + scope: ['with_leave_types', 'with_bank_holidays'], + order: [ + [{ model: model.BankHoliday, as: 'bank_holidays' }, 'date'], + [{ model: model.LeaveType, as: 'leave_types' }, 'name'] + ] + }) - var model = req.app.get('db_model'); - var company; + // Fetch the schedule + const schedule = await company.promise_schedule() - req.user.getCompany({ - include : [ - { model : model.BankHoliday, as : 'bank_holidays' }, - { model : model.LeaveType, as : 'leave_types' }, - ], - order : [ - [{model: model.BankHoliday, as : 'bank_holidays'}, 'date' ], - [{model: model.LeaveType, as : 'leave_types'}, 'name' ], - ], - }) - .then(function(c){ - company = c; - return company.promise_schedule(); - }) - .then(function(schedule){ + // Render the response res.render('general_settings', { - company : company, - schedule : schedule, - countries : config.get('countries'), - }); - }); -}); - -router.post('/company/', function(req, res){ + company, + leave_types: company.leave_types, + schedule: { + works_monday: schedule.get('monday'), + works_tuesday: schedule.get('tuesday'), + works_wednesday: schedule.get('wednesday'), + works_thursday: schedule.get('thursday'), + works_friday: schedule.get('friday'), + works_saturday: schedule.get('saturday'), + works_sunday: schedule.get('sunday') + }, + countries: config.get('countries'), + timezones_available: moment_tz.tz.names(), + title: 'Settings | TimeOff', + carryOverOptions: getAvailableCarriedOverOptions(), + yearCurrent: moment.utc().year(), + yearPrev: moment + .utc() + .subtract(1, 'year') + .year() + }) + } catch (error) { + console.error('An error occurred while fetching general settings:', error) + req.session.flash_error('Failed to load general settings') + res.redirect_with_session('/settings/general/') + } +}) + +router.post('/company/', (req, res) => { + const name = req.body.name && validator.trim(req.body.name) + const country_code = req.body.country && validator.trim(req.body.country) + const date_format = + req.body.date_format && validator.trim(req.body.date_format) + const timezone = req.body.timezone && validator.trim(req.body.timezone) + const carriedOverDays = + req.body.carry_over && validator.trim(req.body.carry_over) + const share_all_absences = + (req.body.share_all_absences && + validator.toBoolean(req.body.share_all_absences)) || + false + const isTeamViewHidden = + (req.body.is_team_view_hidden && + validator.toBoolean(req.body.is_team_view_hidden)) || + false + + if (!validator.isAlphanumeric(country_code)) { + req.session.flash_error('Country should contain only letters and numbers') + } - var name = validator.trim(req.param('name')), - country_code = validator.trim(req.param('country')), - date_format = validator.trim(req.param('date_format')), - share_all_absences= validator.toBoolean( - req.param('share_all_absences') - ); + if (!moment_tz.tz.names().find(tz_str => tz_str === timezone)) { + req.session.flash_error('Time zone is unknown') + } - if (!validator.isAlphanumeric(country_code)){ - req.session.flash_error('Country should contain only letters and numbers'); - } + if (!validator.isNumeric(carriedOverDays)) { + req.session.flash_error('Carried over allowance has to be a number') + } - // In case of validation error redirect back to edit form - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('/settings/general/'); - } + // In case of validation error redirect back to edit form + if (req.session.flash_has_errors()) { + return res.redirect_with_session('/settings/general/') + } - req.user.getCompany() + req.user + .getCompany() // Validate provided date format - .then(function(company){ - - if ( _.indexOf( company.get_available_date_formats(), date_format ) < 0 ) { - var error_msg = 'Unknown date format was provided'; - req.session.flash_error(error_msg); - throw new Error(error_msg); + .then(company => { + if (_.indexOf(company.get_available_date_formats(), date_format) < 0) { + const error_msg = 'Unknown date format was provided' + req.session.flash_error(error_msg) + throw new Error(error_msg) } - return Promise.resolve( company ); + return Promise.resolve(company) }) - .then(function(company){ - company.name = name; - company.country = country_code; - company.share_all_absences= share_all_absences; - company.date_format = date_format; + .then(company => { + company.name = name + company.country = country_code + company.share_all_absences = share_all_absences + company.date_format = date_format + company.timezone = timezone + company.carry_over = carriedOverDays + company.is_team_view_hidden = isTeamViewHidden - return company.save(); + return company.save() }) - .then(function(){ - req.session.flash_message('Company was successfully updated'); - return res.redirect_with_session('/settings/general/'); + .then(() => { + req.session.flash_message('Company was successfully updated') + return res.redirect_with_session('/settings/general/') }) - .catch(function(error){ - console.error( - 'An error occurred when trying to edit company for user ' + req.user.id - + ' : ' + error - ); + .catch(error => { + console.log( + `An error occurred when trying to edit company for user ${ + req.user.id + }: ${error}`, + error.stack + ) - req.session.flash_error( - 'Failed to update company details, please contact customer service' - ); + req.session.flash_error( + 'Failed to update company details, please contact customer service' + ) - return res.redirect_with_session('/settings/general/'); - }); -}); + return res.redirect_with_session('/settings/general/') + }) +}) -router.post('/schedule/', function(req, res){ +router.post('/carryOverUnusedAllowance/', (req, res) => { + req.user + .getCompany() + .then(company => company.getUsers()) + .then(users => calculateCarryOverAllowance({ users })) + .then(() => + req.session.flash_message( + 'Unused allowance was successfully carried over' + ) + ) + .catch(error => { + const logMarker = uuidv4() + console.log( + `[${logMarker}] An error occurred while trying to carry over unused allowance by user ${ + req.user.id + }: ${error} at ${error.stack}` + ) + req.session.flash_error( + `Failed to carry over unused allowances, please contact customer service and provide incident ID: ${logMarker}` + ) + }) + .finally(() => res.redirect_with_session('/settings/general/')) +}) + +router.get('/schedule', async (req, res) => { + try { + const model = req.app.get('db_model') + const company = await req.user.getCompany() + const schedule = await company.promise_schedule() + + res.render('schedule_template', { + schedule: { + works_monday: schedule.get('monday'), + works_tuesday: schedule.get('tuesday'), + works_wednesday: schedule.get('wednesday'), + works_thursday: schedule.get('thursday'), + works_friday: schedule.get('friday'), + works_saturday: schedule.get('saturday'), + works_sunday: schedule.get('sunday') + } + }) + } catch (error) { + console.error( + 'An error occurred while fetching schedule: ' + error, + error.stack + ) + req.session.flash_error('Failed to load schedule') + res.redirect_with_session('/settings/general/') + } +}) - var company, schedule, user, - model = req.app.get('db_model'); +router.post('/schedule/', (req, res) => { + let company + let schedule + let user + const model = req.app.get('db_model') - req.user.getCompany() + req.user + .getCompany() // Obtain scheduler object - .then(function(c){ - company = c; + .then(c => { + company = c - if ( ! req.body.user_id) { + if (!req.body.user_id) { // We are dealing with company wide schedule: easy - return company.promise_schedule(); + return company.promise_schedule() } // Rest is attempt to fetch user specific schedule for given user - return company.getUsers({ - where : { - id : validator.trim( req.body.user_id ), - } - }) - .then(function(u){ - user = u.pop(); - - if ( ! user) { - throw new Error( - "Failed to find user "+req.body.user_id+" for company "+company.id - ); - } - - return user.promise_schedule_I_obey(); - }) - .then(function(sch){ - if (sch.is_user_specific()) { - // User specific schedule exists in database - return Promise.resolve(sch); - } + return company + .getUsers({ + where: { + id: validator.trim(req.body.user_id) + } + }) + .then(u => { + user = u.pop() - // No user specific schedule in database: create in memory default instance - return model.Schedule - .promise_to_build_default_for({ user_id : user.id }); - }); + if (!user) { + throw new Error( + 'Failed to find user ' + + req.body.user_id + + ' for company ' + + company.id + ) + } + + return user.promise_schedule_I_obey() + }) + .then(sch => { + if (sch.is_user_specific()) { + // User specific schedule exists in database + return Promise.resolve(sch) + } + + // No user specific schedule in database: create in memory default instance + return model.Schedule.promise_to_build_default_for({ + user_id: user.id + }) + }) }) // Update schedule object - .then(function(sch){ - schedule = sch; - - ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] - .forEach(function(day){ schedule.set(day, req.body[day]) }); + .then(sch => { + schedule = sch + ;[ + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'sunday' + ].forEach(day => { + schedule.set(day, req.body[day]) + }) - if (schedule.is_user_specific() && _.has(req.body, 'revoke_user_specific_schedule') ) { - return schedule.destroy(); + if ( + schedule.is_user_specific() && + _.has(req.body, 'revoke_user_specific_schedule') + ) { + return schedule.destroy() } else { - return schedule.save(); + return schedule.save() } }) // Action is successfully done - .then(function(){ - req.session.flash_message( schedule.is_user_specific() - ? 'Schedule for user was saved' - : 'Schedule for company was saved' - ); + .then(() => { + req.session.flash_message( + schedule.is_user_specific() + ? 'Schedule for user was saved' + : 'Schedule for company was saved' + ) }) // Action failed - .catch(function(error){ - console.error('An error occurred while saving schedule: ' + error); - req.session.flash_error( schedule.is_user_specific() - ? 'Failed to save user schedule' - : 'Failed to save company schedule' - ); - + .catch(error => { + console.error( + 'An error occurred while saving schedule: ' + error, + error.stack + ) + req.session.flash_error( + schedule.is_user_specific() + ? 'Failed to save user schedule' + : 'Failed to save company schedule' + ) }) // Depending on context redirect user to particular page - .finally(function(){ - res.redirect_with_session(schedule.is_user_specific() - ? (user ? '/users/edit/'+user.id+'/schedule/' : '/users/') - : '/settings/general/' - ); - }); -}); - - -router.get('/departments/', function(req, res) { - - // Add JS that is specific only to current page - res.locals.custom_java_script.push('/js/departments.js'); - - var company_for_template, - model = req.app.get('db_model'); - - req.user.getCompany({ - include : [{ - model : model.User, - as : 'users', - where : model.User.get_active_user_filter(), - }], - order : [ - [ {model : model.User, as : 'users'}, 'lastname' ] - ] - }) - .then(function(company){ - company_for_template = company; - return company.getDepartments({ - include : [ - { model : model.User, as : 'users' }, - ], - // Explicitly order departments as all actions with them rely on - // their order within current company - order : [ - [ model.Department.default_order_field() ] - ], - }); - }) - .then(function(departments){ - - var allowance_options = [{ value : 0, caption : 'None'}], - allowance = 0.5; - while (allowance <= 50) { - allowance_options.push( {value : allowance, caption : allowance} ); - allowance = allowance + 0.5; - } - - res.render('departments', { - title : 'Departments settings', - departments : departments, - company : company_for_template, - allowance_options : allowance_options, - }); - }); -}); - -router.post('/departments/', function(req, res){ - - var name = validator.trim(req.param('name')), - country_code = validator.trim(req.param('country')), - model = req.app.get('db_model'); - - req.user.getCompany({ - include : [ - {model : model.Department, as : 'departments'}, - {model : model.User, as : 'users'} - ], - order : [ - [ {model : model.Department, as : 'departments'}, model.Department.default_order_field() ] - ], - }) - - .then(function(company){ - - var promise_new_department = Promise.resolve(1); - - if (validator.trim(req.param('name__new'))) { - var attributes = get_and_validate_department({ - req : req, - suffix : 'new', - company : company, - department_name : 'New department' - }); - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } - attributes.companyId = company.id; - promise_new_department = model.Department.create(attributes); - } - - return Promise.all([ - promise_new_department, - _.map( - - company.departments, - function(department, index){ - - var attributes = get_and_validate_department({ - req : req, - suffix : index, - company : company, - department_name : department.name, - }); - - // If there were any validation errors: do not update department - // (it affects all departments, that is if one department failed - // validation - all departments are not to be updated) - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } - - return department.updateAttributes(attributes); - } - - ) // End of map that create department update promises - ]); + .finally(() => { + res.redirect_with_session( + schedule.is_user_specific() + ? user + ? '/users/edit/' + user.id + '/schedule/' + : '/users/' + : '/settings/general/' + ) }) +}) - .then(function(){ - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('/settings/departments/'); - } else { - req.session.flash_message('Changes to departments were saved'); - return res.redirect_with_session('/settings/departments/'); - } - }) +router.post('/leavetypes', (req, res) => { + const model = req.app.get('db_model') - .catch(function(error){ - console.error( - 'An error occurred when trying to edit departments by user '+req.user.id - + ' : ' + error - ); - - req.session.flash_error( - 'Failed to update departments details, please contact customer service' - ); - - return res.redirect_with_session('/settings/departments/'); - }); -}); - -router.post('/departments/delete/:department_number/', function(req, res){ - - // department_number is a index number of department to be removed based - // on the list of department on the page, this is not an ID - var department_number = req.param('department_number'); - - var model = req.app.get('db_model'); - - if (!validator.isInt(department_number)) { - console.error( - 'User '+req.user.id+' submited non-int department number ' - +department_number - ); - - req.session.flash_error('Cannot remove department: wronge parameters'); - - return res.redirect_with_session('/settings/departments/'); - } - - req.user.getCompany({ - include : [ - { - model : model.Department, - as : 'departments', - include : { - model : model.User, - as : 'users', - } - }, - ], - order : [ - [ {model : model.Department, as : 'departments'}, model.Department.default_order_field() ] - ], - }) - .then(function(company){ - var department_to_remove = company.departments[ department_number ]; - - // Check if user specify valid department number - if (! department_to_remove) { - - console.error( - 'User '+req.user.id+' tried to remove non-existing department number' - +department_number+' out of '+company.departments.length - ); - - req.session.flash_error('Cannot remove department: wronge parameters'); - - throw new Error( - 'User '+req.user.id+' tried to remove non-existing department number' - +department_number+' out of '+company.departments.length - ); - } - - if (department_to_remove.users.length > 0){ - req.session.flash_error( - 'Cannot remove department '+department_to_remove.name - +' as it still has ' - +department_to_remove.users.length+' users.' - ); - - throw new Error('Department still has users'); + req.user + .get_company_with_all_leave_types() + .then(company => { + let promise_new_leave_type = Promise.resolve(1) + + if (req.body.name__new && validator.trim(req.body.name__new)) { + const attributes = get_and_validate_leave_type({ + req, + suffix: 'new', + item_name: 'New Leave Type' + }) + attributes.company_id = company.id + promise_new_leave_type = model.LeaveType.create(attributes) } - return department_to_remove.destroy(); + return Promise.all([ + promise_new_leave_type, + _.map(company.leave_types, (leave_type, index) => { + const attributes = get_and_validate_leave_type({ + req, + suffix: leave_type.id, + item_name: leave_type.name + }) + + // Update leave type only if there are attributes submitted for it + return attributes ? leave_type.update(attributes) : Promise.resolve(1) + }) // End of map that create leave type update promises + ]) }) - .then(function(){ - req.session.flash_message('Department was successfully removed'); - return res.redirect_with_session('/settings/departments/'); + .then(() => { + req.session.flash_message('Changes to leave types were saved') + return res.redirect_with_session('/settings/general/') }) - .catch(function(error){ - + .catch(error => { console.error( - 'An error occurred when trying to edit departments by user '+req.user.id - + ' : ' + error - ); - - return res.redirect_with_session('/settings/departments/'); - }); -}); - -router.post('/bankholidays/', function(req,res){ - var name = validator.trim(req.param('name')), - date = validator.trim(req.param('date')), - model= req.app.get('db_model'); - - req.user.getCompany({ - include : [{ model : model.BankHoliday, as : 'bank_holidays' }], - order : [[{model: model.BankHoliday, as : 'bank_holidays'}, 'date' ]], - }) - .then(function(company){ - - var promise_new_bank_holiday = Promise.resolve(1); - - if (validator.trim(req.param('name__new'))) { - var attributes = get_and_validate_bank_holiday({ - req : req, - suffix : 'new', - item_name : 'New Bank Holiday' - }); - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } - attributes.companyId = company.id; - promise_new_bank_holiday = model.BankHoliday.create(attributes); - - } - - return Promise.all([ - promise_new_bank_holiday, - _.map( - - company.bank_holidays, - function(bank_holiday, index){ - - var attributes = get_and_validate_bank_holiday({ - req : req, - suffix : index, - item_name : bank_holiday.name, - }); - - // If there were any validation errors: do not update bank holiday - // (it affects all bank holidays, that is if one failed - // validation - all bank holidays are not to be updated) - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } + 'An error occurred when trying to edit Leave types by user ' + + req.user.id + + ' : ', + error, + error.stack + ) + + if (error.user_message) { + req.session.flash_error(error.user_message) + } - return bank_holiday.updateAttributes(attributes); - } + req.session.flash_error( + 'Failed to update leave types details, please contact customer service' + ) - ) // End of map that create bank_holiday update promises - ]); + return res.redirect_with_session('/settings/general/') }) - .then(function(){ - - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('/settings/general/'); - } else { - req.session.flash_message('Changes to bank holidays were saved'); - return res.redirect_with_session('/settings/general/'); - } - }) - .catch(function(error){ - console.error( - 'An error occurred when trying to edit Bank holidays by user '+req.user.id - + ' : ' + error - ); - - req.session.flash_error( - 'Failed to update bank holidayes details, please contact customer service' - ); - - return res.redirect_with_session('/settings/general/'); - }); - -}); - - -router.post('/bankholidays/delete/:bank_holiday_number/', function(req, res){ - - // bank_holiday_number is a index number of bank_holiday to be removed based - // on the list of bank holidays on the page, this is not an ID - var bank_holiday_number = req.param('bank_holiday_number'); - - var model = req.app.get('db_model'); - - if (!validator.isInt(bank_holiday_number)) { - console.error( - 'User '+req.user.id+' submited non-int bank holiday number ' - +bank_holiday_number - ); - - req.session.flash_error('Cannot remove bank holiday: wronge parameters'); +}) - return res.redirect_with_session('/settings/general/'); - } +router.post('/leavetypes/delete/:leave_type_id/', (req, res) => { + const leave_type_id = req.params.leave_type_id - req.user.getCompany({ - include : [{ model : model.BankHoliday, as : 'bank_holidays' }], - order : [[{model: model.BankHoliday, as : 'bank_holidays'}, 'date' ]], - }) - .then(function(company){ - var bank_holiday_to_remove = company.bank_holidays[ bank_holiday_number ]; + const model = req.app.get('db_model') - // Check if user specify valid department number - if (! bank_holiday_to_remove) { + if ( + typeof leave_type_id !== 'number' && + (!leave_type_id || !validator.isInt(leave_type_id)) + ) { + console.error( + 'User ' + + req.user.id + + ' submited non-int leave_type number ' + + leave_type_id + ) - console.error( - 'User '+req.user.id+' tried to remove non-existing bank holiday number' - +bank_holiday_number+' out of '+company.bank_holidays.length - ); + req.session.flash_error('Cannot remove leave_type: wrong parameters') - req.session.flash_error('Cannot remove bank holiday: wronge parameters'); + return res.redirect_with_session('/settings/general/') + } - return res.redirect_with_session('/settings/general/'); + req.user + .getCompany({ + include: [ + { + model: model.LeaveType, + as: 'leave_types', + include: [{ model: model.Leave, as: 'leaves' }] } - - return bank_holiday_to_remove.destroy(); - }) - .then(function(){ - req.session.flash_message('Bank holiday was successfully removed'); - return res.redirect_with_session('/settings/general/'); - }); -}); - -router.post('/leavetypes', function(req, res){ - - var model = req.app.get('db_model'); - - req.user.getCompany({ - include : [{ model : model.LeaveType, as : 'leave_types' }], - order : [[{model: model.LeaveType, as : 'leave_types'}, 'name' ]], + ], + order: [[{ model: model.LeaveType, as: 'leave_types' }, 'name']] }) - .then(function(company){ - - var promise_new_leave_type = Promise.resolve(1); - - if (validator.trim(req.param('name__new'))) { - var attributes = get_and_validate_leave_type({ - req : req, - suffix : 'new', - item_name : 'New Leave Type' - }); - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } - attributes.companyId = company.id; - promise_new_leave_type = model.LeaveType.create(attributes); - } - - return Promise.all([ - promise_new_leave_type, - _.map( + .then(company => { + const leave_type_to_remove = company.leave_types.find( + lt => String(lt.id) === String(leave_type_id) + ) - company.leave_types, - function(leave_type, index){ + // Check if user specify valid department number + if (!leave_type_to_remove) { + req.session.flash_error('Cannot remove leave type: wronge parameters') - var attributes = get_and_validate_leave_type({ - req : req, - suffix : index, - item_name : leave_type.name, - }); + throw new Error( + 'User ' + + req.user.id + + ' tried to remove non-existing leave type number' + + leave_type_id + + ' out of ' + + company.leave_types.length + ) - // If there were any validation errors: do not update leave type - // (it affects all leave types, that is if one failed - // validation - all leave types are not to be updated) - if ( req.session.flash_has_errors() ) { - return Promise.resolve(1); - } + // Check if there exist leaves for current type and if so, do not remove it + } else if (leave_type_to_remove.leaves.length > 0) { + req.session.flash_error('Cannot remove leave type: type is in use') - return leave_type.updateAttributes(attributes); - } + throw new Error('Failed to remove Leave type because it is in used.') + } - ) // End of map that create leave type update promises - ]); + return leave_type_to_remove.destroy() }) - .then(function(){ - if ( ! req.session.flash_has_errors() ) { - req.session.flash_message('Changes to leave types were saved'); - } - return res.redirect_with_session('/settings/general/'); + .then(() => { + req.session.flash_message('Leave type was successfully removed') + return res.redirect_with_session('/settings/general/') }) - .catch(function(error){ - console.error( - 'An error occurred when trying to edit Leave types by user '+req.user.id - + ' : ' + error - ); - - req.session.flash_error( - 'Failed to update leave types details, please contact customer service' - ); - - return res.redirect_with_session('/settings/general/'); - }); - -}); - - -router.post('/leavetypes/delete/:leave_type_number/', function(req, res){ - - // leave_type_number is an index number of leave_type to be removed based - // on the list of leave types on the page, this is not an ID - var leave_type_number = req.param('leave_type_number'); - - var model = req.app.get('db_model'); - - if (!validator.isInt(leave_type_number)) { - console.error( - 'User '+req.user.id+' submited non-int leave_type number ' - +bank_holiday_number - ); - - req.session.flash_error('Cannot remove leave_type: wronge parameters'); + .catch(error => { + console.error( + 'An error occurred when trying to remove leave type by user' + + req.user.id + + ' : ' + + error, + error.stack + ) - return res.redirect_with_session('/settings/general/'); - } + req.session.flash_error('Failed to remove Leave Type') - req.user.getCompany({ - include : [{ - model : model.LeaveType, - as : 'leave_types', - include : [{model: model.Leave, as: 'leaves'}], - }], - order : [[{model: model.LeaveType, as : 'leave_types'}, 'name' ]], + return res.redirect_with_session('/settings/general/') }) - .then(function(company){ - var leave_type_to_remove = company.leave_types[ leave_type_number ]; - - // Check if user specify valid department number - if (! leave_type_to_remove) { - - req.session.flash_error('Cannot remove leave type: wronge parameters'); +}) - throw new Error( - 'User '+req.user.id+' tried to remove non-existing leave type number' - +leave_type_number+' out of '+company.leave_types.length - ); - - - // Check if there exist leaves for current type and if so, do not remove it - } else if (leave_type_to_remove.leaves.length > 0) { - - req.session.flash_error('Cannot remove leave type: type is in use'); - - throw new Error('Failed to remove Leave type because it is in used.'); - } - - return leave_type_to_remove.destroy(); - }) - .then(function(){ - req.session.flash_message('Leave type was successfully removed'); - return res.redirect_with_session('/settings/general/'); +router.get('/company/integration-api/', (req, res) => { + req.user.getCompany().then(company => + res.render('settings_company_integration_api', { + company }) - .catch(function(error){ - console.error( - 'An error occurred when trying to remove leave type by user' + req.user.id - + ' : ' + error - ); + ) +}) - req.session.flash_error( - 'Failed to remove Leave Type' - ); +router.post('/company/integration-api/', (req, res) => { + const featureIsEnabled = validator.toBoolean(req.body.integration_api_enabled) - return res.redirect_with_session('/settings/general/'); - }); -}); + let action = req.user.getCompany() -/* - * Export all data related to current user's Company, - * the output data structure is suitable to for feeding - * into company restore action. - * - * */ -router.post('/company/export/', function(req, res){ - - req.user - .get_company_for_export() - .then(function(company){ + action = action.then(company => { + company.set('integration_api_enabled', featureIsEnabled) + return company.save() + }) - res.attachment( - company.name_for_machine()+'_'+moment().format('YYYYMMDD-hhmmss')+'.json' - ); + if (req.body.regenerate_token) { + action = action.then(company => company.regenerateIntegrationApiToken()) + } - res.send(JSON.stringify(company)); - }); -}); + action = action.then(() => { + req.session.flash_message('Settings were saved') -router.get('/company/authentication/', function(req, res){ + return res.redirect_with_session('./') + }) - req.user - .getCompany() - .then(function(company){ - res.render('settings_company_authentication', { - company : company, - ldap_config : company.get('ldap_auth_config'), - }); - }); -}); + action.catch(error => { + console.log( + `Failed to save Integration API configuration, reason: ${extractSystemErrorMessage( + error + )}` + ) + req.session.flash_error( + `Failed to save settings. ${extractUserErrorMessage(error)}` + ) -router.post('/company/authentication/', function(req, res){ + return res.redirect_with_session('./') + }) +}) + +router.get('/company/authentication/', (req, res) => { + req.user.getCompany().then(company => { + res.render('settings_company_authentication', { + company, + ldap_config: company.get('ldap_auth_config'), + title: 'Settings - Authentication | TimeOff' + }) + }) +}) +router.post('/company/authentication/', (req, res) => { req.user .getCompany() - .then(function(company){ - - var parameters = get_and_validate_ldap_auth_configuration({ - req : req, - }); + .then(company => { + const parameters = get_and_validate_ldap_auth_configuration({ + req + }) // Updaye in memory Company object but do not save changes until new LDAP // configuration checked against current user // (this is needed to prevent situation when admin by lock herself out) - company.set('ldap_auth_config', parameters.ldap_config); - company.setDataValue('ldap_auth_enabled', parameters.ldap_auth_enabled); + company.set('ldap_auth_config', parameters.ldap_config) + company.setDataValue('ldap_auth_enabled', parameters.ldap_auth_enabled) - var ldap_server = company.get_ldap_server(); + const ldap_server = company.get_ldap_server() - var auth_func = Promise.promisify(ldap_server.authenticate.bind(ldap_server)); + // Handle event based errors from ldapauth-fork + let ldapError = '' + ldap_server.on('error', err => { + ldapError = err + }) - return auth_func(req.user.email, parameters.password_to_check) - .then(function(){ - return company.save(); + function auth_func(email, password) { + return new Promise((resolve, reject) => { + // Wait one second before cheking for event based errors + setTimeout(() => { + if (ldapError) { + reject(ldapError) + } + }, 1000) + + ldap_server.authenticate(email, password, (error, user) => { + if (error) { + reject(error) + } else { + resolve(user) + } + }) }) - .catch(function(error){ + } + + return auth_func(req.user.email, parameters.password_to_check) + .then(() => company.save()) + .catch(error => { error = new Error( - "Failed to validate new LDAP settings with provided current user password. "+error - ); - error.show_to_user = true; - throw error; - }); + 'Failed to validate new LDAP settings with provided current user password. ' + + error, + error.stack + ) + error.show_to_user = true + throw error + }) }) - .then(function(){ - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('/settings/company/authentication/'); + .then(() => { + if (req.session.flash_has_errors()) { + return res.redirect_with_session('/settings/company/authentication/') } else { - req.session.flash_message('LDAP configuration was updated'); - return res.redirect_with_session('/settings/company/authentication/'); + req.session.flash_message('LDAP configuration was updated') + return res.redirect_with_session('/settings/company/authentication/') } }) - .catch(function(error){ + .catch(error => { console.error( - "An error occured while trying to update LDAP configuration: %s", error - ); + 'An error occured while trying to update LDAP configuration: %s', + error, + error.stack + ) req.session.flash_error( 'Failed to update LDAP configuration. ' + - ( error.show_to_user ? 'Error: '+error : 'Please contact customer service') - ); + (error.show_to_user ? error : 'Please contact customer service') + ) - return res.redirect_with_session('/settings/company/authentication/'); - }); -}); + return res.redirect_with_session('/settings/company/authentication/') + }) +}) -function get_and_validate_department(args) { - var req = args.req, - index = args.suffix, - company = args.company, - department_name = args.department_name; +function get_and_validate_leave_type(args) { + const req = args.req + const suffix = args.suffix + const item_name = args.item_name // Get user parameters - var name = validator.trim(req.param('name__'+index)), - allowance = validator.trim(req.param('allowance__'+index)), - include_public_holidays = validator.toBoolean( - req.param('include_public_holidays__'+index) - ), - boss_id = validator.trim(req.param('boss_id__'+index)); + const name = + req.body['name__' + suffix] && validator.trim(req.body['name__' + suffix]) + const color = + (req.body['color__' + suffix] && + validator.trim(req.body['color__' + suffix])) || + '#ffffff' + const limit = + (req.body['limit__' + suffix] && + validator.trim(req.body['limit__' + suffix])) || + 0 + const first_record = + (req.body.first_record && validator.trim(req.body.first_record)) || 0 + const use_allowance = + (req.body['use_allowance__' + suffix] && + validator.toBoolean(req.body['use_allowance__' + suffix])) || + false + const use_personal = + (req.body['use_personal__' + suffix] && + validator.toBoolean(req.body['use_personal__' + suffix])) || + false + const auto_approve = + (req.body['auto_approve__' + suffix] && + validator.toBoolean(req.body['auto_approve__' + suffix])) || + false + const manager_only = + (req.body['manager_only__' + suffix] && + validator.toBoolean(req.body['manager_only__' + suffix])) || + false + + // If no name for leave type was provided: do nothing - treat case + // as no need to update the leave type + if (!name) { + return false + } + + // VPP TODO move that into resusable component + function throw_user_error(message) { + const error = new Error(message) + error.user_message = message + throw error + } // Validate provided parameters - // - // New allowance should be from range of (0;50] - if (!validator.isFloat(allowance)) { - req.session.flash_error( - 'New allowance for '+department_name+' should be numeric' - ); - } else if (!((0 <= allowance) && (allowance <= 50))) { - req.session.flash_error( - 'New allowance for '+department_name+' should be between 0.5 and 50 days' - ); + if (!validator.isHexadecimal(color.replace('#', ''))) { + throw_user_error( + 'New color for ' + + item_name + + ' should be a valid hexadecimal color value' + ) } - // New manager ID should be numeric and from within - // current company - if (!validator.isNumeric( boss_id ) ) { - req.session.flash_error( - 'New boss reference for '+department_name+' should be numeric' - ); - } else if (_.contains( - _.map( - company.users, function(user){ return user.id; }), - boss_id - )) { - req.session.flash_error( - 'New boss for '+department_name+' is unknown' - ); + + if (typeof limit !== 'number' && (!limit || !validator.isNumeric(limit))) { + throw_user_error('New limit for ' + item_name + ' should be a valid number') + } else if (limit < 0) { + throw_user_error( + 'New limit for ' + item_name + ' should be a positive number or 0' + ) } return { - name : name, - allowance : allowance, - include_public_holidays : include_public_holidays, - bossId : boss_id, - }; + name, + color, + use_allowance, + use_personal, + auto_approve, + manager_only, + private_leave, + limit, + sort_order: first_record && String(first_record) === String(suffix) ? 1 : 0 + } } -function get_and_validate_bank_holiday(args) { - var req = args.req, - index = args.suffix, - item_name = args.item_name; - - // Get user parameters - var name = validator.trim(req.param('name__'+index)), - date = validator.trim(req.param('date__'+index)); +function get_and_validate_ldap_auth_configuration({ req }) { + // Get parameters + // + const url = validator.trim(req.param('url') + '') + const binddn = validator.trim(req.param('binddn') + '') + const bindcredentials = validator.trim(req.param('bindcredentials') + '') + const searchbase = validator.trim(req.param('searchbase') + '') + const searchfilter = validator.trim(req.param('searchfilter') + '') + const ldap_auth_enabled = validator.toBoolean( + req.param('ldap_auth_enabled') + '' + ) + const allow_unauthorized_cert = validator.toBoolean( + req.param('allow_unauthorized_cert') + '' + ) + // Fetch the password of current user that is valid in LDAP system + const password_to_check = validator.trim(req.body.password_to_check + '') // Validate provided parameters - // - // Note, we allow users to put whatever they want into the name. - // The XSS defence is in the templates - date = req.user.company.normalise_date( date ); + if (!validator.matches(url, /^ldaps?:\/\/[a-z0-9\.\-]+:\d+$/i)) { + req.session.flash_error( + "URL to LDAP server must be of following format: 'ldap://HOSTNAME:PORT'" + ) + } - if ( ! validator.isDate(date) ) { + if (!validator.matches(searchfilter, /\{\{username\}\}/)) { req.session.flash_error( - 'New day for '+item_name+' should be date' - ); + "LDAP filter must contain the {{username}} placeholder. Use '(mail={{username}})' to match mail as the username." + ) + } + + if (req.session.flash_has_errors()) { + const error = new Error('Validation failed') + error.show_to_user = true + throw error } + // Return the configuration object return { - name : name, - date : date, - }; + ldap_config: { + url, + binddn, + bindcredentials, + searchbase, + searchfilter, + allow_unauthorized_cert + }, + ldap_auth_enabled, + password_to_check + } } -function get_and_validate_leave_type(args) { - var req = args.req, - index = args.suffix, - item_name = args.item_name; +router.get('/company/backup/', (req, res) => { + const companyExporter = new CompanyExporter({ + dbSchema: req.app.get('db_model') + }) - // Get user parameters - var name = validator.trim(req.param('name__'+index)), - color = validator.trim(req.param('color__'+index)) || '#f1f1f1', - limit = validator.trim(req.param('limit__'+index)) || 0, - use_allowance = validator.toBoolean( - req.param('use_allowance__'+index) - ); + let company - // Validate provided parameters + req.user + .getCompany() + .then(c => Promise.resolve((company = c))) - if ( ! validator.isHexColor(color)) { - req.session.flash_error( - 'New color for '+item_name+' should be color code' - ); - } - if ( ! validator.isNumeric(limit) ){ - req.session.flash_error( - 'New limit for '+item_name+' should be a valide number' - ); - } else if ( limit < 0) { - req.session.flash_error( - 'New limit for '+item_name+' should be positive number or 0' - ); - } + // Generate company summary + .then(company => companyExporter.promiseCompanySummary({ company })) - return { - name : name, - color : color, - use_allowance : use_allowance, - limit : limit, - }; -} + // Get CSV presentation of company summary + .then(companySummary => companySummary.promise_as_csv_string()) -function get_and_validate_ldap_auth_configuration(args) { - var req = args.req; + .then(csv_content => { + res.attachment(company.name_for_machine() + '_backup.csv') - // Get parameters - // - var url = validator.trim(req.param('url')), - binddn = validator.trim(req.param('binddn')), - bindcredentials = validator.trim(req.param('bindcredentials')), - searchbase = validator.trim(req.param('searchbase')), - ldap_auth_enabled = validator.toBoolean(req.param('ldap_auth_enabled')), + res.send(csv_content) + }) + .catch(error => { + console.error( + 'An error occured while downloading company summary: %s, at %s', + error, + error.stack + ) - // Fetch the password of current user that is valid in LDAP system - password_to_check = validator.trim(req.param('password_to_check')); + req.session.flash_error( + 'Failed to download company summary. ' + + (error.show_to_user ? error : 'Please contact customer service') + ) - // Validate provided parameters + return res.redirect_with_session('/settings/general/') + }) +}) - if (!validator.matches(url, /^ldaps?:\/\/[a-z0-9\.\-]+:\d+$/i)){ - req.session.flash_error( - "URL to LDAP server must be of following format: 'ldap://HOSTNAME:PORT'" - ); - } +router.post('/company/delete/', (req, res) => { + let company - if ( req.session.flash_has_errors() ) { - var error = new Error("Validation failed"); - error.show_to_user = true; - throw error; - } + req.user + .getCompany() + .then(c => Promise.resolve((company = c))) + .then(company => + CompanyRemover.promiseToRemove({ + company, + byUser: req.user, + confirmName: req.body.confirm_name + }) + ) + .then(() => { + req.session.flash_message( + `Company ${company.name} and related data were successfully removed` + ) - // Return the configuration object - return { - ldap_config : { - url : url, - binddn : binddn, - bindcredentials : bindcredentials, - searchbase : searchbase, - }, - ldap_auth_enabled : ldap_auth_enabled, - password_to_check : password_to_check, - }; -} + return res.redirect_with_session('/') + }) + .catch(error => { + console.log( + `Failed to remove ${company.id} by user ${ + req.user.id + }. Reason: ${Exception.extract_system_error_message(error)}, at ${ + error.stack + }` + ) + + req.session.flash_error( + `Failed to remove company. Reason: ${Exception.extract_user_error_message( + error + )}` + ) + + return res.redirect_with_session('/settings/general/') + }) +}) + +const getAvailableCarriedOverOptions = () => [ + { days: 0, label: 'None' }, + ...[...Array(21).keys()].filter(i => i > 0).map(i => ({ days: i, label: i })), + { days: 1000, label: 'All' } +] -module.exports = router; +module.exports = router diff --git a/lib/route/users.js b/lib/route/users.js deleted file mode 100644 index 144f1e786..000000000 --- a/lib/route/users.js +++ /dev/null @@ -1,726 +0,0 @@ - -"use strict"; - -var express = require('express'), - router = express.Router(), - validator = require('validator'), - Promise = require('bluebird'), - moment = require('moment'), - _ = require('underscore'), - uuid = require('node-uuid'), - EmailTransport = require('../email'); - -// Make sure that current user is authorized to deal with settings -router.all(/.*/, require('../middleware/ensure_user_is_admin')); - -router.get('/add/', function(req, res){ - req.user - .get_company_for_add_user() - .then(function(company){ - res.render('user_add', { - company : company, - }); - }); -}); - -router.post('/add/', function(req, res){ - - var current_company, - model = req.app.get('db_model'); - - req.user - .get_company_for_add_user() - .then(function(company){ - - current_company = company; - - var new_user_attributes = get_and_validate_user_parameters({ - req : req, - item_name : 'user', - departments : company.departments, - // If current company has LDAP auth do not require password - require_password : (company.ldap_auth_enabled ? false : true ), - }); - - // TODO: make department responsible for adding new useR? - new_user_attributes.password = model.User.hashify_password( - // ... Instead use random string as password - // (users will be able to reset it when needed) - new_user_attributes.password || uuid.v4() - ); - new_user_attributes.companyId = company.id; - - return Promise.resolve(new_user_attributes); - }) - - // Make sure that we do not add user with existing emails - .then(function(new_user_attributes){ - return model.User.find_by_email(new_user_attributes.email) - .then(function(user){ - - if (user) { - req.session.flash_error('Email is already in use'); - throw new Error('Email is already used'); - } - - return Promise.resolve(new_user_attributes); - }); - }) - - .then(function(new_user_attributes){ - return model.User.create(new_user_attributes); - }) - - .then(function(new_user){ - var Email = new EmailTransport(); - - return Email.promise_add_new_user_email({ - company : current_company, - admin_user : req.user, - new_user : new_user, - }); - }) - - .then(function(){ - if ( req.session.flash_has_errors() ) { - return res.redirect_with_session('../add/'); - } else { - req.session.flash_message('New user account successfully added'); - return res.redirect_with_session('../'); - } - }) - - .catch(function(error){ - console.error( - 'An error occurred when trying to add new user account by user '+req.user.id - + ' : ' + error - ); - - req.session.flash_error( - 'Failed to add new user' - ); - - return res.redirect_with_session('../add/'); - }); -}); - -router.get('/edit/:user_id/', function(req, res){ - var user_id = validator.trim(req.param('user_id')); - - // Add JS that is specific only to current page - res.locals.custom_java_script.push( - '/js/inittooltips.js' - ); - - Promise.try(function(){ - ensure_user_id_is_integer({req : req, user_id : user_id}); - }) - .then(function(){ - return req.user.get_company_for_user_details({ - user_id : user_id, - }); - }) - .then(function(company){ - - var employee = company.users[0]; - - return employee.promise_schedule_I_obey() - .then(function(){ - res.render('user_details', { - company : company, - employee : employee, - show_main_tab : true, - }); - }); - }) - .catch(function(error){ - console.error( - 'An error occurred when trying to open employee details by user '+req.user.id - + ' : ' + error - ); - - return res.redirect_with_session('../../'); - }); -}); - -router.get('/edit/:user_id/absences/', function(req, res){ - var user_id = validator.trim(req.param('user_id')); - - // Add JS that is specific only to current page - res.locals.custom_java_script.push( - '/js/inittooltips.js' - ); - - Promise.try(function(){ - ensure_user_id_is_integer({req : req, user_id : user_id}); - }) - .then(function(){ - return req.user.get_company_for_user_details({ - user_id : user_id, - }); - }) - .then(function(company){ - - var employee = company.users[0]; - - return employee - .promise_schedule_I_obey() - .then(function(){ - employee - .promise_my_active_leaves_ever({}) - .then(function(leaves){ - res.render('user_details', { - employee : employee, - leaves : leaves, - show_absence_tab : true, - }); - }); - }); - }) - .catch(function(error){ - console.error( - 'An error occurred when trying to open employee absences by user '+req.user.id - + ' : ' + error - ); - - return res.redirect_with_session('../../../'); - }); -}); - - -router.get('/edit/:user_id/schedule/', function(req, res){ - var user_id = validator.trim(req.param('user_id')); - - Promise.try(function(){ - ensure_user_id_is_integer({req : req, user_id : user_id}); - }) - .then(function(){ - return req.user.get_company_for_user_details({ - user_id : user_id, - }); - }) - .then(function(company){ - - var employee = company.users[0]; - - return employee - .promise_schedule_I_obey() - .then(function(schedule){ - res.render('user_details', { - employee : employee, - schedule : schedule, - show_schedule_tab : true, - }); - }); - }) - .catch(function(error){ - console.error( - 'An error occurred when trying to open employee absences by user '+req.user.id - + ' : ' + error - ); - - return res.redirect_with_session('../../../'); - }); -}); - - -// Special step performed while savinf existing employee accont details -// -// In case when employee had "end date" populated and now it is going -// to be updated to be in future - check if during the time user was inactive -// new user was added (including other companies) -// -var ensure_user_was_not_useed_elsewhere_while_being_inactive = function(args){ - var - employee = args.employee, - new_user_attributes = args.new_user_attributes, - req = args.req, - model = args.model; - - if ( - // Employee has end_date defined - employee.end_date && - ( - ! new_user_attributes.end_date - || - ( - // new "end_date" is provided - // new "end_date" is in future - new_user_attributes.end_date && - moment( new_user_attributes.end_date ).startOf('day').toDate() >= moment().startOf('day').toDate() - ) - ) - ) { - return model.User.find_by_email(new_user_attributes.email) - .then(function(user){ - - if (user && user.companyId !== employee.companyId) { - var error_msg = 'There is an active account with similar email somewhere within system.'; - req.session.flash_error(error_msg); - throw new Error(error_msg); - } - - return Promise.resolve(); - }); - } - - return Promise.resolve(); -}; - -// Extra step: in case when employee is going to have new email, -// check that it is not duplicated -// -var ensure_email_is_not_used_elsewhere = function(args){ - var - employee = args.employee, - new_user_attributes = args.new_user_attributes, - req = args.req, - model = args.model; - - if (new_user_attributes.email === employee.email) { - return Promise.resolve(); - } - - return model.User - .find_by_email(new_user_attributes.email) - .then(function(user){ - - if (user) { - req.session.flash_error('Email is already in use'); - throw new Error('Email is already used'); - } - - return Promise.resolve(); - }); -}; - -var ensure_we_are_not_removing_last_admin = function(args){ - var - employee = args.employee, - new_user_attributes = args.new_user_attributes, - req = args.req, - model = args.model; - - if ( - // It is about to change admin rights - new_user_attributes.admin !== employee.admin - // and it is revoking admin rights - && ! new_user_attributes.admin - ) { - return model.User - .count({ where : { - companyId : employee.companyId, - id : { $ne : employee.id}, - admin : true, - }}) - .then(function(number_of_admins_to_be_left){ - if (number_of_admins_to_be_left > 0) { - return Promise.resolve(); - } - - req.session.flash_error('This is last admin witihn company. Cannot revoke admin rights.'); - throw new Error('Attempt to revoke admin rights from last admin in comapny '+employee.companyId); - }); - } - - return Promise.resolve(); -}; - -router.post('/edit/:user_id/', function(req, res){ - var user_id = validator.trim(req.param('user_id')); - - var new_user_attributes, - employee, - model = req.app.get('db_model'); - - Promise.try(function(){ - ensure_user_id_is_integer({req : req, user_id : user_id}); - }) - .then(function(){ - return req.user.get_company_for_user_details({ - user_id : user_id, - }); - }) - .then(function(company){ - - new_user_attributes = get_and_validate_user_parameters({ - req : req, - item_name : 'user', - departments : company.departments, - }); - - if (new_user_attributes.password) { - new_user_attributes.password = model.User.hashify_password( - new_user_attributes.password - ); - } - - employee = company.users[0]; - - return Promise.resolve(); - }) - - // Ensure that new email if it was changed is not used anywhere else - // withing system - .then(function(){ return ensure_email_is_not_used_elsewhere({ - employee : employee, - new_user_attributes : new_user_attributes, - req : req, - model : model, - })}) - - // Double check user in case it is re-activated - .then(function(){ return ensure_user_was_not_useed_elsewhere_while_being_inactive({ - employee : employee, - new_user_attributes : new_user_attributes, - req : req, - model : model, - })}) - - .then(function(){ return ensure_we_are_not_removing_last_admin({ - employee : employee, - new_user_attributes : new_user_attributes, - req : req, - model : model, - })}) - - // All validations are passed: update database - .then(function(){ - - employee.updateAttributes(new_user_attributes).then(function(){ - req.session.flash_message( - 'Details for '+employee.full_name()+' were updated' - ); - return res.redirect_with_session(req.body.back_to_absences ? './absences/' : '.'); - }); - }) - - .catch(function(error){ - console.error( - 'An error occurred when trying to save chnages to user account by user '+req.user.id - + ' : ' + error - ); - - req.session.flash_error( - 'Failed to save changes.' - ); - - return res.redirect_with_session(req.body.back_to_absences ? './absences/' : '.'); - }); -}); - - -router.post('/delete/:user_id/', function(req, res){ - var user_id = validator.trim(req.param('user_id')); - - Promise.try(function(){ - ensure_user_id_is_integer({req : req, user_id : user_id}); - }) - .then(function(){ - return req.user.get_company_for_user_details({ - user_id : user_id, - }); - }) - .then(function(company){ - - var employee = company.users[0]; - - - return employee.remove(); - - }) - .then(function(result){ - req.session.flash_message( - 'Employee records were removed from the system' - ); - return res.redirect_with_session('../..'); - }) - .catch(function(error){ - console.error( - 'An error occurred when trying to remove user '+user_id+' by user ' - + req.user.id + '. Error: ' + error - ); - - req.session.flash_error( - 'Failed to remove user. ' + error - ); - - return res.redirect_with_session('../../edit/'+user_id+'/'); - }); -}); - - -router.all('/search/', function(req, res){ - - // Currently we support search only by email and only JSON type requests - if ( ! req.accepts('json')) { - // redirect client to the users index page - return res.redirect_with_session('../'); - } - - var email = validator.trim( req.param('email') ).toLowerCase(); - - if ( ! validator.isEmail( email )) { - req.session.flash_error('Provided email does not look like valid one: "'+email+'"'); - return res.json([]); - } - - // search for users only related to currently login admin - // - var promise_result = req.user.getCompany({ - include : [{ - model : req.app.get('db_model').User, - as : 'users', - where : { - email : email - } - }] - }); - - promise_result.then(function(company){ - if (company.users.length > 0) { - res.json(company.users) - } else { - res.json([]); - } - }); -}); - - -/* Handle the root for users section, it shows the list of all users - * */ -router.get('/', function(req, res) { - - var department_id = req.param('department'), - users_filter = {}, - model = req.app.get('db_model'); - - if (validator.isNumeric( department_id )) { - users_filter = { DepartmentId : department_id }; - } else { - department_id = undefined; - } - - req.user.getCompany({ - include : [ - { - model : model.User, - as : 'users', - where : users_filter, - required : false, - include : [ - { model : model.Department, as : 'department' }, - // Following is needed to be able to calculate how many days were - // taken from allowance - { - model : model.Leave, - as : 'my_leaves', - required : false, - where : { - // status : model.Leave.status_approved(), - status : [ - model.Leave.status_approved(), - model.Leave.status_new(), - model.Leave.status_pended_revoke(), - ], - $or : { - date_start : { - $between : [ - moment().startOf('year').format('YYYY-MM-DD'), - moment().endOf('year').format('YYYY-MM-DD'), - ] - }, - date_end : { - $between : [ - moment().startOf('year').format('YYYY-MM-DD'), - moment().endOf('year').format('YYYY-MM-DD'), - ] - } - } - }, - include : [{ - model : model.LeaveType, - as : 'leave_type', - }, - ] // End of my_leaves include - } - ], - }, - ], - order : [ - [{ model : model.User, as : 'users' }, 'lastname'], - [ - { model : model.User, as : 'users' }, - { model : model.Department, as : 'department'}, - model.Department.default_order_field() - ], - ] - }) - - // Make sure that objects have all necessary attributes to render page - // (template system is sync only) - .then(function(company){ - return company.getBank_holidays() - // stick bank holidays to company - .then(function(bank_holidays){ - company.bank_holidays = bank_holidays; - return company.getDepartments({ - order : [ model.Department.default_order_field() ], - }); - }) - // stick departments to company as well - .then(function(departments){ - company.departments = departments; - return Promise.resolve(company); - }) - }) - - // Make sure that user's leaves have reference back to user in question - .then(function(company){ - company.users.forEach(function(user){ - user.company = company; - user.my_leaves.forEach(function(leave){ leave.user = user }); - }); - - return Promise.resolve(company); - }) - - // Update users to have neccessary data for leave calculations - .then(function(company){ - return Promise.resolve(company.users).map(function(user){ - return user.promise_schedule_I_obey(); - },{ - concurrency : 10, - }) - .then(function(){ return Promise.resolve(company) }); - }) - - .then(function(company){ - res.render('users', { - title : company.name + "'s people", - users : company.users, - company : company, - department_id : Number(department_id), - }); - }); -}); - -function get_and_validate_user_parameters(args) { - var req = args.req, - item_name = args.item_name, - require_password = args.require_password || false; - - // Get user parameters - var name = validator.trim(req.param('name')), - lastname = validator.trim(req.param('lastname')), - email = validator.trim(req.param('email_address')), - department_id = validator.trim(req.param('department')), - start_date = validator.trim(req.param('start_date')), - end_date = validator.trim(req.param('end_date')), - adjustment = validator.trim(req.param('adjustment')) || 0, - password = validator.trim(req.param('password_one')), - password_confirm = validator.trim(req.param('password_confirm')), - admin = validator.toBoolean(req.param('admin')), - auto_approve = validator.toBoolean(req.param('auto_approve')); - - // Validate provided parameters - - if (!validator.isEmail(email)) { - req.session.flash_error( - 'New email of '+item_name+' should be valid email address' - ); - } - - if (!validator.isNumeric(department_id)) { - req.session.flash_error( - 'New department number of '+item_name+' should be a valid number' - ); - } - - if (adjustment && ! validator.isFloat(adjustment) ) { - req.session.flash_error( - 'New allowance adjustment of '+item_name+' should be a valid number' - ); - } else if (adjustment && ! ( adjustment % 1 === 0 || Math.abs( adjustment % 1 ) === 0.5 )) { - req.session.flash_error( - 'New allowance adjustment of '+item_name+' should be either whole integer number or with half' - ); - } - - start_date = req.user.company.normalise_date( start_date ); - - if (!validator.isDate(start_date)) { - req.session.flash_error( - 'New start date for '+item_name+' should be valid date' - ); - } - - if (end_date ){ - - end_date = req.user.company.normalise_date( end_date ); - - if ( ! validator.isDate(end_date)) { - req.session.flash_error( - 'New end date for '+item_name+' should be valid date' - ); - } - } - - if ( - start_date && - end_date && - moment(start_date).toDate() > moment(end_date).toDate() - ){ - req.session.flash_error( - 'End date for '+item_name+' is before start date' - ); - } - - if (password && password !== password_confirm) { - req.session.flash_error('Confirmed password does not match initial one'); - } - - if (require_password && ! password) { - req.session.flash_error('Password is required'); - } - - if ( req.session.flash_has_errors() ) { - throw new Error( 'Got validation errors' ); - } - - // Normalize email as we operate only with lower case letters in emails - email = email.toLowerCase(); - - var attributes = { - name : name, - lastname : lastname, - email : email, - DepartmentId : department_id, - start_date : start_date, - end_date : (end_date || null), - adjustment : adjustment, - admin : admin, - auto_approve : auto_approve, - }; - - if ( password ) { - attributes.password = password; - } - - return attributes; -} - -function ensure_user_id_is_integer(args){ - var req = args.req, - user_id = args.user_id; - - if (! validator.isInt(user_id)){ - throw new Error( - 'User '+req.user.id+' tried to edit user with non-integer ID: '+user_id - ); - } - - return; -} - -module.exports = router; diff --git a/lib/route/users/index.js b/lib/route/users/index.js new file mode 100644 index 000000000..9cc937f8b --- /dev/null +++ b/lib/route/users/index.js @@ -0,0 +1,1176 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const validator = require('validator') +const Promise = require('bluebird') +const moment = require('moment') +const csv = Promise.promisifyAll(require('csv')) +const fs = require('fs') +const formidable = require('formidable') +const LeaveCollectionUtil = require('../../model/leave_collection')() +const Exception = require('../../error') +const UserImporter = require('../../model/user_importer') +const EmailTransport = require('../../email') +const { getAuditCaptureForUser } = require('../../model/audit') +const SlackTransport = require('../../slack') +const multer = require('multer') +const Sequelize = require('sequelize') +const Op = Sequelize.Op + +const upload = multer({ + limits: { fileSize: 2097152 } // for 2MB limit +}) + +const { sorter } = require('../../util') +// Make sure that current user is authorized to deal with settings +router.all(/.*/, require('../../middleware/ensure_user_is_admin')) + +router.get('/add/', (req, res) => { + req.user.get_company_for_add_user().then(company => { + res.render('user_add', { + company, + departments: company.departments.sort((a, b) => sorter(a.name, b.name)), + title: 'Add new user | TimeOff' + }) + }) +}) + +router.post('/add/', (req, res) => { + const Email = new EmailTransport() + const Slack = new SlackTransport() + + let current_company, new_user_attributes + + req.user + .get_company_for_add_user() + .then(company => { + current_company = company + + new_user_attributes = get_and_validate_user_parameters({ + req, + params: req.body, + item_name: 'user', + departments: company.departments, + // If current company has LDAP auth do not require password + require_password: !company.ldap_auth_enabled + }) + + return Promise.resolve() + }) + + // Make sure that we do not add user with existing emails + .then(() => + UserImporter.validate_email_to_be_free({ + email: new_user_attributes.email + }) + ) + + // Add new user to database + .then(() => + UserImporter.add_user({ + name: new_user_attributes.name, + slack_username: new_user_attributes.slack_username, + lastname: new_user_attributes.lastname, + email: new_user_attributes.email, + department_id: new_user_attributes.department_id, + start_date: new_user_attributes.start_date, + end_date: new_user_attributes.end_date, + admin: new_user_attributes.admin, + manager: new_user_attributes.manager, + auto_approve: new_user_attributes.auto_approve, + company_id: req.user.company_id, + password: new_user_attributes.password + }) + ) + + .then(new_user => + Email.promise_add_new_user_email({ + company: current_company, + admin_user: req.user, + new_user + }) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send the email to ' + new_user.email + ' : ' + error, + error.stack + ) + return Promise.resolve(new_user) + }) + ) + + .then(new_user => + Slack.promise_add_new_user_slack({ + company: current_company, + admin_user: req.user, + new_user + }) + // Fail silently for the user and track the error for the administrator. + .catch(error => { + console.error( + 'Failed to send the slack message to ' + + new_user.email + + ' : ' + + error, + error.stack + ) + return Promise.resolve(new_user) + }) + ) + + .then(() => { + if (req.session.flash_has_errors()) { + return res.redirect_with_session('../add/') + } + req.session.flash_message('New user account successfully added') + return res.redirect_with_session('../') + }) + + .catch(error => { + console.log( + 'An error occurred when trying to add new user account by user ' + + req.user.id, + error.stack + ) + console.dir(error) + + if (error && error.tom_error) { + req.session.flash_error(Exception.extract_user_error_message(error)) + } + + req.session.flash_error('Failed to add new user') + + return res.redirect_with_session('../add/') + }) +}) + +router.get('/import/', (req, res) => { + req.user.getCompany().then(company => + res.render('users_import', { + company, + title: 'Import users | TimeOff' + }) + ) +}) + +router.post('/import/', upload.single('users_import'), async (req, res) => { + try { + if (!req.file) { + throw new Error('No .CSV file to restore from was provided') + } + + console.log('adding users via import') + const csv_data_string = req.file.buffer.toString('utf8') + const parsed_data = await csv.parseAsync(csv_data_string, { trim: true }) + + if (parsed_data.length > 201) { + req.session.flash_error( + 'Cannot import more than 200 employees per one go.' + ) + throw new Error('Cannot import more than 200 employees per one go.') + } + + const action_result = await UserImporter.add_users_in_bulk({ + to_company_id: req.user.company_id, + bulk_header: parsed_data.shift(), + bulk_data: parsed_data + }) + + // Handle action_result as needed... + console.log('Success: ', action_result) + req.session.flash_message('Users imported successfully') + return res.redirect_with_session('/users/import/') + } catch (error) { + // Handle error... + console.log('Error importing: ', error) + req.session.flash_error('Failed to import users') + return res.redirect_with_session('/users/import/') + } +}) + +router.post('/import-sample/', (req, res) => { + req.user + .getCompany({ + scope: ['with_active_users', 'with_simple_departments'] + }) + .then(company => { + res.attachment(company.name_for_machine() + '.csv') + + const content = company.users.map(user => [ + user.email, + user.slack_username, + user.lastname, + user.name, + company.departments.find(dep => dep.id === user.department_id).name + ]) + + content.unshift([ + 'email', + 'slack_username', + 'lastname', + 'name', + 'department' + ]) + + return csv.stringifyAsync(content) + }) + .then(csv_data_string => res.send(csv_data_string)) +}) + +router.get('/edit/:user_id/', (req, res) => { + const user_id = validator.trim(req.params.user_id) + + Promise.try(() => ensure_user_id_is_integer({ req, user_id })) + .then(() => req.user.get_company_for_user_details({ user_id })) + .then(company => { + const employee = company.users[0] + + return employee.promise_schedule_I_obey().then(() => { + res.render('user_details', { + company, + employee, + show_main_tab: true, + departments: company.departments.sort((a, b) => + sorter(a.name, b.name) + ), + title: 'Edit user | TimeOff' + }) + }) + }) + .catch(error => { + console.error( + 'An error occurred when trying to open employee details by user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + return res.redirect_with_session('../../') + }) +}) + +router.get('/edit/:user_id/absences/', (req, res) => { + const user_id = validator.trim(req.params.user_id) + let user_allowance + + const dbModel = req.app.get('db_model') + + const year = validator.isNumeric(req.query.year || req.body.year || '') + ? moment.utc(req.query.year || req.body.year, 'YYYY') + : req.user.company.get_today() + + Promise.try(() => ensure_user_id_is_integer({ req, user_id })) + .then(() => req.user.get_company_for_user_details({ user_id })) + .then(company => { + const employee = company.users[0] + return employee.reload_with_session_details() + }) + .then(employee => employee.reload_with_leave_details({ year })) + .then(employee => + Promise.join( + employee + .promise_allowance({ year }) + .then(allowance_obj => + Promise.resolve([(user_allowance = allowance_obj), employee]) + ), + + employee.promise_adjustment_for_year(year.format('YYYY')), + + employee.promise_carried_over_allowance_for_year( + year.format('YYYY') + ), + + (args, employee_adjustment, carried_over_allowance) => { + args.push(null) + args.push(employee_adjustment) + args.push(carried_over_allowance) + return Promise.resolve(args) + } + ) + ) + .then(args => { + const allowance_obj = args[0] + const remaining_allowance = + allowance_obj.total_number_of_days_in_allowance - + allowance_obj.number_of_days_taken_from_allowance + const employee = args[1] + const total_days_number = allowance_obj.total_number_of_days_in_allowance + const employee_adjustment = args[3] + const carried_over_allowance = args[4] + + const leave_statistics = { + total_for_current_year: total_days_number, + remaining: remaining_allowance + } + + leave_statistics.used_so_far = + allowance_obj.number_of_days_taken_from_allowance + + leave_statistics.used_so_far_percent = + leave_statistics.total_for_current_year > 0 + ? (100 * leave_statistics.used_so_far) / + leave_statistics.total_for_current_year + : 0 + + leave_statistics.remaining_percent = + leave_statistics.total_for_current_year > 0 + ? (100 * + (leave_statistics.total_for_current_year - + leave_statistics.used_so_far)) / + leave_statistics.total_for_current_year + : 0 + + return employee.promise_schedule_I_obey().then(() => { + employee + .promise_my_active_leaves_ever({}) + .then(leaves => + LeaveCollectionUtil.enrichLeavesWithComments({ leaves, dbModel }) + ) + .then(leaves => LeaveCollectionUtil.promise_to_group_leaves(leaves)) + .then(grouped_leaves => { + res.render('user_details', { + employee, + grouped_leaves, + show_absence_tab: true, + leave_type_statistics: employee.get_leave_statistics_by_types(), + leave_statistics, + employee_adjustment, + carried_over_allowance, + user_allowance, + title: 'Edit user | TimeOff', + // Add the year variables here + previous_year: moment.utc(year).add(-1, 'year').format('YYYY'), + current_year: year.format('YYYY'), + next_year: moment.utc(year).add(1, 'year').format('YYYY') + }) + }) + }) + }) + .catch(error => { + console.error( + 'An error occurred when trying to open employee absences by user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + return res.redirect_with_session('../../../') + }) +}) + +router.get('/edit/:user_id/schedule/', async (req, res) => { + const user_id = validator.trim(req.params.user_id) + + try { + ensure_user_id_is_integer({ req, user_id }) + + const company = await req.user.get_company_for_user_details({ user_id }) + + if (!company.users || company.users.length === 0) { + throw new Error(`User with ID ${user_id} not found in company`) + } + + const employee = company.users[0] + const schedule = await employee.promise_schedule_I_obey() + const is_user_specific = schedule.is_user_specific() + + return res.render('user_details', { + employee, + schedule: { + works_monday: schedule.get('monday'), + works_tuesday: schedule.get('tuesday'), + works_wednesday: schedule.get('wednesday'), + works_thursday: schedule.get('thursday'), + works_friday: schedule.get('friday'), + works_saturday: schedule.get('saturday'), + works_sunday: schedule.get('sunday') + }, + user_specific_schedule: is_user_specific, + show_schedule_tab: true, + title: 'User - Schedule | TimeOff' + }) + } catch (error) { + console.error( + `An error occurred when trying to open employee absences by user ${ + req.user.id + } : ${error}`, + error.stack + ) + + return res.redirect_with_session('../../../') + } +}) + +router.get('/edit/:user_id/calendar/', async (req, res) => { + const user_id = validator.trim(req.params.user_id) + + const year = validator.isNumeric(req.query.year || req.body.year || '') + ? moment.utc(req.query.year || req.body.year, 'YYYY') + : req.user.company.get_today() + + let employee, calendar, companyEnriched, supervisors, userAllowance + + try { + await ensure_user_id_is_integer({ req, user_id }) + + const company = await req.user.get_company_for_user_details({ + user_id + }) + + employee = company.users[0] + + await employee.reload_with_session_details() + + calendar = await employee.promise_calendar({ + year: year.clone(), + show_full_year: true + }) + companyEnriched = await employee.get_company_with_all_leave_types() + employee = await employee.reload_with_leave_details({ year }) + supervisors = await employee.promise_supervisors() + userAllowance = await employee.promise_allowance({ year }) + } catch (error) { + console.error( + `An error ocurred while trying to render Calendar for user [${user_id}]: ${error} at ${ + error.stack + }` + ) + + return res.redirect_with_session('../../../') + } + + const fullLeaveTypeStatistics = employee.get_leave_statistics_by_types() + + res.render('user_details', { + employee, + show_calendar_tab: true, + + calendar: calendar.map(c => c.as_for_template()), + company: companyEnriched, + current_user: employee, + supervisors, + previous_year: moment + .utc(year) + .add(-1, 'year') + .format('YYYY'), + current_year: year.format('YYYY'), + next_year: moment + .utc(year) + .add(1, 'year') + .format('YYYY'), + show_full_year: true, + user_allowance: userAllowance, + leave_type_statistics: fullLeaveTypeStatistics.filter( + st => st.days_taken > 0 + ) + }) +}) + +// Special step performed while saving existing employee account details +// +// In case when employee had "end date" populated and now it is going +// to be updated to be in future - check if during the time user was inactive +// new user was added (including other companies) +// +function ensure_user_was_not_useed_elsewhere_while_being_inactive(args) { + const employee = args.employee + const new_user_attributes = args.new_user_attributes + const req = args.req + const model = args.model + + if ( + // Employee has end_date defined + employee.end_date && + (!new_user_attributes.end_date || + // new "end_date" is provided + // new "end_date" is in future + (new_user_attributes.end_date && + moment + .utc(new_user_attributes.end_date) + .startOf('day') + .toDate() >= + req.user.company + .get_today() + .startOf('day') + .toDate())) + ) { + return model.User.find_by_email(new_user_attributes.email).then(user => { + if (user && user.company_id !== employee.company_id) { + const error_msg = + 'There is an active account with similar email somewhere within system.' + req.session.flash_error(error_msg) + throw new Error(error_msg) + } + + return Promise.resolve() + }) + } + + return Promise.resolve() +} + +// Extra step: in case when employee is going to have new email, +// check that it is not duplicated +// +function ensure_email_is_not_used_elsewhere(args) { + const employee = args.employee + const new_user_attributes = args.new_user_attributes + const req = args.req + const model = args.model + + if (new_user_attributes.email === employee.email) { + return Promise.resolve() + } + + return model.User.find_by_email(new_user_attributes.email).then(user => { + if (user) { + req.session.flash_error('Email is already in use') + throw new Error('Email is already used') + } + + return Promise.resolve() + }) +} + +function ensure_we_are_not_removing_last_admin(args) { + const employee = args.employee + const new_user_attributes = args.new_user_attributes + const req = args.req + const model = args.model + + if ( + // It is about to change admin rights + new_user_attributes.admin !== employee.admin && + // and it is revoking admin rights + !new_user_attributes.admin + ) { + return model.User.count({ + where: { + company_id: employee.company_id, + id: { [Op.ne]: employee.id }, + admin: true + } + }).then(number_of_admins_to_be_left => { + if (number_of_admins_to_be_left > 0) { + return Promise.resolve() + } + + req.session.flash_error( + 'This is last admin within company. Cannot revoke admin rights.' + ) + throw new Error( + 'Attempt to revoke admin rights from last admin in comapny ' + + employee.company_id + ) + }) + } + + return Promise.resolve() +} + +router.post('/edit/:user_id/', (req, res) => { + const user_id = validator.trim(req.params.user_id) + + let new_user_attributes + let employee + const model = req.app.get('db_model') + + const year = validator.isNumeric(req.query.year || req.body.year || '') + ? moment.utc(req.query.year || req.body.year, 'YYYY') + : req.user.company.get_today() + + Promise.try(() => { + ensure_user_id_is_integer({ req, user_id }) + }) + .then(() => + req.user.get_company_for_user_details({ + user_id + }) + ) + .then(company => { + new_user_attributes = get_and_validate_user_parameters({ + req, + params: req.body, + item_name: 'user', + departments: company.departments + }) + + if (new_user_attributes.password) { + new_user_attributes.password = model.User.hashify_password( + new_user_attributes.password + ) + } + + employee = company.users[0] + + return Promise.resolve() + }) + + // Ensure that new email if it was changed is not used anywhere else + // withing system + .then(() => + ensure_email_is_not_used_elsewhere({ + employee, + new_user_attributes, + req, + model + }) + ) + + // Double check user in case it is re-activated + .then(() => + ensure_user_was_not_useed_elsewhere_while_being_inactive({ + employee, + new_user_attributes, + req, + model + }) + ) + + .then(() => + ensure_we_are_not_removing_last_admin({ + employee, + new_user_attributes, + req, + model + }) + ) + + // All validations are passed: update database + .then(() => { + const adjustment = new_user_attributes.adjustment + delete new_user_attributes.adjustment + + const captureAuditTrail = getAuditCaptureForUser({ + byUser: req.user, + forUser: employee.get({ plain: true }), + newAttributes: new_user_attributes + }) + + employee + + // Update user record + .update(new_user_attributes) + + .then(() => captureAuditTrail()) + + // Update adjustment if necessary + .then(() => { + if (adjustment !== undefined) { + return employee.promise_to_update_adjustment({ + year: year.format('YYYY'), // Use the selected year + adjustment + }) + } + + return Promise.resolve() + }) + + .then(() => { + req.session.flash_message( + 'Details for ' + employee.full_name() + ' were updated' + ) + return res.redirect_with_session( + req.body.back_to_absences ? './absences/?year=' + year.format('YYYY') : '.?year=' + year.format('YYYY') + ) + }) + }) + + .catch(error => { + console.error( + 'An error occurred when trying to save changes to user account by user ' + + req.user.id + + ' : ' + + error, + error.stack + ) + + req.session.flash_error('Failed to save changes.') + + return res.redirect_with_session( + req.body.back_to_absences ? './absences/' : '.' + ) + }) +}) + +router.post('/delete/:user_id/', (req, res) => { + const user_id = validator.trim(req.params.user_id) + let auditCapture + Promise.try(() => ensure_user_id_is_integer({ req, user_id })) + .then(() => req.user.get_company_for_user_details({ user_id })) + .then(company => { + const employee = company.users[0] + const employeePlain = employee.get({ plain: true }) + auditCapture = getAuditCaptureForUser({ + byUser: req.user, + forUser: employeePlain, + newAttributes: Object.assign( + {}, + ...Object.keys(employeePlain).map(k => ({ [k]: null })) + ) + }) + return employee.remove() + }) + .then(() => auditCapture()) + .then(result => { + req.session.flash_message('Employee records were removed from the system') + return res.redirect_with_session('../..') + }) + .catch(error => { + console.error( + 'An error occurred when trying to remove user ' + + user_id + + ' by user ' + + req.user.id + + '. Error: ' + + error, + error.stack + ) + + req.session.flash_error('Failed to remove user. ' + error) + + return res.redirect_with_session('../../edit/' + user_id + '/') + }) +}) + +router.all('/search/', (req, res) => { + // Currently we support search only by email and only JSON type requests + if (!req.accepts('json')) { + // redirect client to the users index page + return res.redirect_with_session('../') + } + + const email = validator.trim(req.body.email || req.query.email).toLowerCase() + + if (!validator.isEmail(email)) { + req.session.flash_error( + 'Provided email does not look like valid one: "' + email + '"' + ) + return res.json([]) + } + + // search for users only related to currently login admin + // + const promise_result = req.user.getCompany({ + include: [ + { + model: req.app.get('db_model').User, + as: 'users', + where: { + email + } + } + ] + }) + + promise_result.then(company => { + if (company.users.length > 0) { + res.json(company.users) + } else { + res.json([]) + } + }) +}) + +/* Handle the root for users section, it shows the list of all users + * */ +router.get('/', (req, res) => { + let department_id = req.query.department + let users_filter = {} + const model = req.app.get('db_model') + + if ( + typeof department_id === 'number' || + (department_id && validator.isNumeric(department_id)) + ) { + users_filter = { department_id } + } else { + department_id = undefined + } + + req.user + .getCompany({ + include: [ + { + model: model.User, + as: 'users', + where: users_filter, + required: false, + include: [ + { model: model.Department, as: 'department' }, + // Following is needed to be able to calculate how many days were + // taken from allowance + { + model: model.Leave, + as: 'my_leaves', + required: false, + where: { + // status : model.Leave.status_approved(), + status: [ + model.Leave.status_approved(), + model.Leave.status_new(), + model.Leave.status_pended_revoke() + ], + [Op.or]: { + date_start: { + [Op.between]: [ + moment + .utc() + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc() + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + }, + date_end: { + [Op.between]: [ + moment + .utc() + .startOf('year') + .format('YYYY-MM-DD'), + moment + .utc() + .endOf('year') + .format('YYYY-MM-DD 23:59:59') + ] + } + } + }, + include: [ + { + model: model.LeaveType, + as: 'leave_type' + } + ] // End of my_leaves include + } + ] + } + ], + order: [ + [{ model: model.User, as: 'users' }, 'lastname'], + [ + { model: model.User, as: 'users' }, + { model: model.Department, as: 'department' }, + model.Department.default_order_field() + ] + ] + }) + + // Make sure that objects have all necessary attributes to render page + // (template system is sync only) + .then(company => + company + .getBank_holidays() + // stick bank holidays to company + .then(bank_holidays => { + company.bank_holidays = bank_holidays + return company.getDepartments({ + order: [model.Department.default_order_field()] + }) + }) + // stick departments to company as well + .then(departments => { + company.departments = departments.sort((a, b) => + sorter(a.name, b.name) + ) + return Promise.resolve(company) + }) + ) + + // Make sure that user's leaves have reference back to user in question + .then(company => { + company.users.forEach(user => { + user.company = company + user.my_leaves.forEach(leave => { + leave.user = user + }) + }) + + return Promise.resolve(company) + }) + + // Update users to have neccessary data for leave calculations + .then(company => + Promise.resolve(company.users) + .map(user => user.promise_schedule_I_obey(), { + concurrency: 10 + }) + .then(() => Promise.resolve(company)) + ) + + /* + * Following block builds array of object for each user in company. + * Each object consist of following keys: + * - user_row : reference to the sequelize user row object + * - number_of_days_available_in_allowance : number of days remaining in allowance for given user + * + * This step is necessary because we are moving to non-blocking API for libraries, + * so we need to get all data before passing it into template as template + * + * */ + .then(company => + Promise.resolve(company.users) + .map( + user => + user.promise_allowance().then(allowance_obj => + Promise.resolve({ + user_row: user, + number_of_days_available_in_allowance: + allowance_obj.number_of_days_available_in_allowance + }) + ), + { + concurrency: 10 + } + ) + .then(users_info => Promise.resolve([company, users_info])) + ) + + // We are moving away from passing complex objects into templates + // for calling complicated methods from within templates + // Now only basic simple objects to be sent over to the template, + // all preparation to be done before rendering. + // + // So prepare special rendering data structure here + .then(args => promise_user_list_data_for_rendering(args)) + + .then(args => { + const company = args[0] + const users_info = args[1] + + if (req.query['as-csv']) { + return users_list_as_csv({ + users_info, + company, + req: res, + res + }) + } + + res.render('users', { + company, + department_id: Number(department_id), + title: company.name + "'s people", + users_info + }) + }) +}) + +function promise_user_list_data_for_rendering(args) { + const company = args[0] + const users_info = args[1] + + const usersInfoForRendering = users_info.map(ui => ({ + user_id: ui.user_row.id, + user_email: ui.user_row.email, + user_slack_username: ui.user_row.slack_username, + user_name: ui.user_row.name, + user_lastname: ui.user_row.lastname, + user_full_name: ui.user_row.full_name(), + department_id: ui.user_row.department.id, + department_name: ui.user_row.department.name, + is_admin: ui.user_row.admin, + is_manager: ui.user_row.manager, + number_of_days_available_in_allowance: + ui.number_of_days_available_in_allowance, + number_of_days_taken_from_allowance: ui.user_row.calculate_number_of_days_taken_from_allowance(), + is_active: ui.user_row.is_active() + })) + + const sortedUsersInfoForRendering = usersInfoForRendering.sort((a, b) => + sorter(a.user_lastname, b.user_lastname) + ) + + return Promise.resolve([company, sortedUsersInfoForRendering]) +} + +function users_list_as_csv(args) { + const users_info = args.users_info + const company = args.company + const res = args.res + + // Compose file name + res.attachment( + company.name_for_machine() + + '_employees_on_' + + company.get_today().format('YYYY_MMM_DD') + + '.csv' + ) + + // Compose result CSV header + const content = [ + [ + 'email', + 'slack_username', + 'lastname', + 'name', + 'department', + 'remaining allowance', + 'days used' + ] + ] + + // ... and body + users_info.forEach(ui => { + content.push([ + ui.user_email, + ui.user_slack_username, + ui.user_lastname, + ui.user_name, + ui.department_name, + ui.number_of_days_available_in_allowance, + ui.number_of_days_taken_from_allowance + ]) + }) + + return csv + .stringifyAsync(content) + .then(csv_data_string => res.send(csv_data_string)) +} + +// TODO: fix sequelize error when updating admin checkbox +function get_and_validate_user_parameters(args) { + const req = args.req + const params = args.params + const item_name = args.item_name + const require_password = args.require_password || false + + // Get user parameters + const name = params.name && validator.trim(params.name) + const lastname = params.lastname && validator.trim(params.lastname) + const slack_username = + params.slack_username && validator.trim(params.slack_username) + let email = params.email_address && validator.trim(params.email_address) + const department_id = params.department && validator.trim(params.department) + let start_date = params.start_date && validator.trim(params.start_date) + let end_date = params.end_date && validator.trim(params.end_date) + const adjustment = params.adjustment && validator.trim(params.adjustment) + const password = params.password_one && validator.trim(params.password_one) + const password_confirm = + params.password_confirm && validator.trim(params.password_confirm) + const admin = (params.admin && validator.toBoolean(params.admin)) || false + const manager = + (params.manager && validator.toBoolean(params.manager)) || false + const auto_approve = + (params.auto_approve && validator.toBoolean(params.auto_approve)) || false + + // Validate provided parameters + if (!email || !validator.isEmail(email)) { + req.session.flash_error( + 'New email of ' + item_name + ' should be valid email address' + ) + } + + if ( + typeof department_id !== 'number' && + (!department_id || !validator.isNumeric(department_id)) + ) { + req.session.flash_error( + 'New department number of ' + item_name + ' should be a valid number' + ) + } + + if (adjustment && !validator.isFloat(adjustment)) { + req.session.flash_error( + 'New allowance adjustment of ' + item_name + ' should be a valid number' + ) + } else if ( + adjustment && + !(adjustment % 1 === 0 || Math.abs(adjustment % 1) === 0.5) + ) { + req.session.flash_error( + 'New allowance adjustment of ' + + item_name + + ' should be either whole integer number or with half' + ) + } + + start_date = req.user.company.normalise_date(start_date) + + if (!start_date || !validator.toDate(start_date)) { + req.session.flash_error( + 'New start date for ' + item_name + ' should be valid date' + ) + } + + if (end_date) { + end_date = req.user.company.normalise_date(end_date) + + if (!end_date || !validator.toDate(end_date)) { + req.session.flash_error( + 'New end date for ' + item_name + ' should be valid date' + ) + } + } + + if ( + start_date && + end_date && + moment.utc(start_date).toDate() > moment.utc(end_date).toDate() + ) { + req.session.flash_error( + 'End date for ' + item_name + ' is before start date' + ) + } + + if (password && password !== password_confirm) { + req.session.flash_error('Confirmed password does not match initial one') + } + + if (require_password && !password) { + req.session.flash_error('Password is required') + } + + if (req.session.flash_has_errors()) { + throw new Error('Got validation errors') + } + + // Normalize email as we operate only with lower case letters in emails + email = email.toLowerCase() + + const attributes = { + name, + lastname, + slack_username, + email, + department_id, + start_date, + end_date: end_date || null, + admin, + manager, + auto_approve + } + + console.log('validator attributes: ', attributes) + + if (adjustment || String(adjustment) === '0') { + attributes.adjustment = adjustment + } + + if (password) { + attributes.password = password + } + + return attributes +} + +function ensure_user_id_is_integer(args) { + const req = args.req + const user_id = args.user_id + + if (typeof user_id !== 'number' && (!user_id || !validator.isInt(user_id))) { + throw new Error( + 'User ' + + req.user.id + + ' tried to edit user with non-integer ID: ' + + user_id + ) + } +} + +module.exports = router diff --git a/lib/route/users/summary.js b/lib/route/users/summary.js new file mode 100644 index 000000000..4f3c24956 --- /dev/null +++ b/lib/route/users/summary.js @@ -0,0 +1,87 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const Exception = require('../../error') +const Promise = require('bluebird') + +router.get('/summary/:userId/', (req, res) => { + const requestor = req.user + const user_id = req.params.userId + console.log('id and requestor is: ', user_id, requestor.id) + + const model = req.app.get('db_model') + // console.log('model is: ', model) + + // Get user object by provided ID + let flow = model.User.scope('withDepartments').findOne({ + where: { id: user_id } + }) + + // Ensure that requestor and user belong to the same company + flow = flow.then(user => { + if (!user || user.company_id !== requestor.company_id) { + throw Exception.throwUserError({ + user_error: 'No access to given user', + system_error: `User ${ + requestor.id + } tried to access details of user ${user_id}: either ID does not belong to existing user of requestor and Id does not share company` + }) + } + + return Promise.resolve(user) + }) + + // If requestor is admin OR is manager for given user show detailed INFO + flow = flow.then(user => { + // In case admin is asking user's details: show detailed version + if (requestor.is_admin()) { + return Promise.resolve({ user, isDetailed: true }) + } + + // Check if given user is among supervised ones + return requestor.promise_supervised_users().then(users => { + let isDetailed = false + + if (users.filter(u => u.id === user.id).length === 1) { + isDetailed = true + } + + return Promise.resolve({ user, isDetailed }) + }) + }) + + flow = flow.then(({ user, isDetailed }) => { + if (!isDetailed) { + return Promise.resolve({ user }) + } + + return Promise.all([ + user.promise_allowance(), + user.promise_supervisors() + ]).then(([userAllowance, supervisors]) => ({ + user, + userAllowance, + supervisors + })) + }) + + flow = flow.then(({ user, userAllowance, supervisors }) => { + const supervisorNames = (supervisors || []).map(u => u.full_name()) + + res.render('user/popup_user_details', { + layout: false, + userFullName: user.full_name(), + departmentName: user.get('department').name, + userAllowance, + supervisorNames + }) + }) + + flow.catch(error => { + console.log(error) + res.send('Failed to get user details...') + }) +}) + +module.exports = router diff --git a/lib/route/utils/pager.js b/lib/route/utils/pager.js index c5cae9edd..d7b477c5d 100644 --- a/lib/route/utils/pager.js +++ b/lib/route/utils/pager.js @@ -9,48 +9,44 @@ * * an attribute "items_per_page" wich * globaly defines how many items per page are shown * * a method "get_pager_object" that return the pager object to be passed into the pager.hbs -* -* Pager object has following entries: -* -* * page_prev : number to address previous page, if 0 then previous page is disabled -* * page_next : page number to next opage, if 0 no more pages after current one -* * page : number of current page, page counting starts with 1 -* * filter : an object that contains other GET parameters to be placed into the -* pager's links; keys are the names of parameters and values are values -* * page_qnty : total number of pages available for current pager -* * count : total number of pages items -* * pages : simple array with item for each page, each item is corresponding page number -* + * + * Pager object has following entries: + * + * * page_prev : number to address previous page, if 0 then previous page is disabled + * * page_next : page number to next opage, if 0 no more pages after current one + * * page : number of current page, page counting starts with 1 + * * filter : an object that contains other GET parameters to be placed into the + * pager's links; keys are the names of parameters and values are values + * * page_qnty : total number of pages available for current pager + * * count : total number of pages items + * * pages : simple array with item for each page, each item is corresponding page number + * * */ -"use strict"; +'use strict' -module.exports = function(){ +module.exports = function() { return { - - items_per_page : 10, - - get_pager_object : function(args){ - - var - total_items_count = args.total_items_count, - filter = args.filter, - page = args.current_page; - - var page_qnty = Math.ceil( total_items_count / this.items_per_page ); - - var pager = { - filter : filter, - page : page, - page_qnty : page_qnty, - page_prev : (page > 1 ? page - 1 : 0), - page_next : (page >= page_qnty ? 0 : page + 1), - count : total_items_count, - pages : Array.apply(null, Array( page_qnty )) - .map(function (x, i) { return i+1; }), - }; - - return pager; - }, - }; -}; + items_per_page: 10, + + get_pager_object: function(args) { + const total_items_count = args.total_items_count + const filter = args.filter + const page = args.current_page + + const page_qnty = Math.ceil(total_items_count / this.items_per_page) + + const pager = { + filter, + page, + page_qnty, + page_prev: page > 1 ? page - 1 : 0, + page_next: page >= page_qnty ? 0 : page + 1, + count: total_items_count, + pages: Array.apply(null, Array(page_qnty)).map((x, i) => i + 1) + } + + return pager + } + } +} diff --git a/lib/route/validator/leave_request.js b/lib/route/validator/leave_request.js index ac2cf72c6..08d9afad8 100644 --- a/lib/route/validator/leave_request.js +++ b/lib/route/validator/leave_request.js @@ -1,86 +1,115 @@ - -'use strict'; - -var validator = require('validator'), - moment = require('moment'), - LeaveRequestParameters = require('../../model/leave_request_parameters'); - -module.exports = function(args){ - var req = args.req; - - var user = validator.trim( req.param('user') ), - leave_type = validator.trim( req.param('leave_type') ), - from_date = validator.trim( req.param('from_date') ), - from_date_part = validator.trim( req.param('from_date_part') ), - to_date = validator.trim( req.param('to_date') ), - to_date_part = validator.trim( req.param('to_date_part') ), - reason = validator.trim( req.param('reason') ); - - if (user && !validator.isNumeric(user)){ - req.session.flash_error('Incorrect employee'); - } - - if (!validator.isNumeric(leave_type)){ - req.session.flash_error('Incorrect leave type'); - } - - var date_validator = function(date_str, label) { - try { - - // Basic check - if (! date_str ) throw new Error("date needs to be defined"); - - date_str = req.user.company.normalise_date(date_str); - - // Ensure that normalisation went OK - if (! validator.isDate(date_str)) throw new Error("Invalid date format"); - - } catch (e) { - console.log('Got an error ' + e); - req.session.flash_error(label + ' should be a date'); +'use strict' + +const validator = require('validator') +const moment = require('moment') +const LeaveRequestParameters = require('../../model/leave_request_parameters') + +module.exports = function(args) { + const req = args.req + const params = args.params + + const user = typeof params.user !== 'undefined' && validator.trim(params.user) + console.log('validating user: ' + user) + const leave_type = + typeof params.leave_type !== 'undefined' && + validator.trim(params.leave_type) + let from_date = + typeof params.from_date !== 'undefined' && validator.trim(params.from_date) + const from_date_part = + typeof params.from_date_part !== 'undefined' && + validator.trim(params.from_date_part) + let to_date = + typeof params.to_date !== 'undefined' && validator.trim(params.to_date) + const to_date_part = + typeof params.to_date_part !== 'undefined' && + validator.trim(params.to_date_part) + const reason = + typeof params.reason !== 'undefined' && validator.trim(params.reason) + + if ( + typeof user !== 'undefined' && + user && + typeof user !== 'number' && + (!user || !validator.isNumeric(user)) + ) { + req.session.flash_error('Incorrect employee') + } + + if ( + typeof leave_type !== 'number' && + (!leave_type || !validator.isNumeric(leave_type)) + ) { + req.session.flash_error('Incorrect leave type') + } + + const date_validator = function(date_str, label) { + try { + // Basic check + if (!date_str) throw new Error('date needs to be defined') + + // log check + console.log('date_str: ' + date_str) + date_str = req.user.company.normalise_date(date_str) + + // Ensure that normalisation went OK + if (date_str.length !== 10) { + throw new Error('Invalid date format') } + } catch (e) { + console.log('Got an error ' + e) + req.session.flash_error(label + ' should be a date') } - - date_validator(from_date, 'From date'); - - if ( !validator.matches(from_date_part, /^[123]$/) - || !validator.matches(to_date_part, /^[123]$/) - ){ - req.session.flash_error('Incorrect day part'); - } - - date_validator(to_date, 'To date'); - - // Check if it makes sence to continue validation (as following code relies on - // to and from dates to be valid ones) - if ( req.session.flash_has_errors() ) { - throw new Error( 'Got validation errors' ); - } - - // Convert dates inot format used internally - from_date = req.user.company.normalise_date(from_date); - to_date = req.user.company.normalise_date(to_date); - - if (from_date.substr(0,4) !== to_date.substr(0,4)) { - req.session.flash_error('Current implementation does not allow inter year leaves. Please split your request into two parts'); - } - - if ( req.session.flash_has_errors() ) { - throw new Error( 'Got validation errors' ); - } - - var valid_attributes = { - leave_type : leave_type, - from_date : from_date, - from_date_part : from_date_part, - to_date : to_date, - to_date_part : to_date_part, - reason : reason, - }; - - if ( user ) { - valid_attributes.user = user; - } - - return new LeaveRequestParameters( valid_attributes ); -}; + } + + date_validator(from_date, 'From date') + + if ( + typeof from_date_part === 'undefined' || + !validator.matches(from_date_part, /^[123]$/) || + typeof to_date_part === 'undefined' || + !validator.matches(to_date_part, /^[123]$/) + ) { + req.session.flash_error('Incorrect day part') + } + + date_validator(to_date, 'To date') + + // Check if it makes sence to continue validation (as following code relies on + // to and from dates to be valid ones) + if (req.session.flash_has_errors()) { + const error = new Error('Got validation errors') + error.flash = req.session.flash + throw error + } + + // Convert dates inot format used internally + from_date = req.user.company.normalise_date(from_date) + to_date = req.user.company.normalise_date(to_date) + + if (from_date.substr(0, 4) !== to_date.substr(0, 4)) { + req.session.flash_error( + 'Current implementation does not allow inter year leaves. Please split your request into two parts' + ) + } + + if (req.session.flash_has_errors()) { + const error = new Error('Got validation errors') + error.flash = req.session.flash + throw error + } + + const valid_attributes = { + leave_type, + from_date, + from_date_part, + to_date, + to_date_part, + reason + } + + if (user) { + valid_attributes.user = user + } + + return new LeaveRequestParameters(valid_attributes) +} diff --git a/lib/slack.js b/lib/slack.js new file mode 100644 index 000000000..54e4e3c04 --- /dev/null +++ b/lib/slack.js @@ -0,0 +1,429 @@ +'use strict' + +const bluebird = require('bluebird') +const handlebars = require('express-handlebars').create({ + partialsDir: __dirname + '/../views/partials/', + extname: '.hbs', + helpers: require('./view/helpers')() +}) +const config = require('./config') +const { WebClient } = require('@slack/client') + +function Slack() {} + +// This is a little helper that ensure that data in context are in a shape +// suitable for usage in templates +// +function _promise_to_unfold_context(context) { + if (context.user) { + return context.user.reload_with_session_details() + } else { + return bluebird.resolve(1) + } +} + +Slack.prototype.promise_rendered_slack_template = function(args) { + const filename = args.template_name + const context = args.context || {} + + return ( + bluebird + .resolve() + + // Prepare context to be passed into first rendering stage + .then(() => _promise_to_unfold_context(context)) + + // Render slack text + .then(() => + handlebars.render( + __dirname + '/../views/slack/' + filename + '.hbs', + context + ) + ) + + .then(text => + bluebird.resolve({ + slack_username: context.slack_username, + text + }) + ) + ) +} + +// If current configuration does not allow sending slacks, it return empty function +// +Slack.prototype.get_send_slack = function() { + const slack_token = config.get('slack:token') + + // Check if current installation is set to send slack notifications + if (!slack_token) { + return function() { + console.debug('Pretend to send slack: ' + JSON.stringify(arguments)) + return bluebird.resolve() + } + } + + const slack_bot_name = config.get('slack:bot_name') || '' + const slack_icon_url = config.get('slack:icon_url') || '' + + const web = new WebClient(slack_token) + + function push_slack(args) { + if (!args.channel) { + console.debug('No username set, skip sending to Slack.') + return Promise.resolve() + } + + console.debug('Sending slack message on channel "' + args.channel + '"') + return web.chat + .postMessage({ + username: slack_bot_name, + channel: args.channel, + icon_url: slack_icon_url, + attachments: [ + { + text: args.text + } + ] + }) + .then(res => { + // `res` contains information about the posted message + console.log('Message sent: ', res.ts) + }) + .catch(console.error) + } + + return push_slack +} + +// Send registration complete slack for provided user + +Slack.prototype.promise_registration_slack = function(args) { + const self = this + const user = args.user + const send_slack = self.get_send_slack() + + return self + .promise_rendered_slack_template({ + template_name: 'registration_complete_slack', + context: { + user, + slack_username: user.slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) +} + +Slack.prototype.promise_add_new_user_slack = function(args) { + const self = this + const company = args.company + const admin_user = args.admin_user + const new_user = args.new_user + const slack_username = args.new_user?.slack_username + const send_slack = self.get_send_slack() + + return self + .promise_rendered_slack_template({ + template_name: 'add_new_user_slack', + context: { + new_user, + admin_user, + company, + user: new_user, + slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) +} + +Slack.prototype.promise_leave_request_revoke_slacks = function(args) { + const self = this + const leave = args.leave + const send_slack = self.get_send_slack() + + let template_name_to_supervisor = 'leave_request_revoke_to_supervisor_slack' + let template_name_to_requestor = 'leave_request_revoke_to_requestor_slack' + + if (leave.get('user').is_auto_approve()) { + template_name_to_supervisor = + 'leave_request_revoke_to_supervisor_autoapprove_slack' + template_name_to_requestor = + 'leave_request_revoke_to_requestor_autoapprove_slack' + } + + const promise_slack_to_supervisor = self + .promise_rendered_slack_template({ + template_name: template_name_to_supervisor, + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver'), + slack_username: leave.get('approver').slack_username + } + }) + .then(slack_obj => { + console.log('Approver') + console.log(slack_obj) + return send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + }) + + const promise_slack_to_requestor = self + .promise_rendered_slack_template({ + template_name: template_name_to_requestor, + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('user'), + slack_username: leave.get('user').slack_username + } + }) + .then(slack_obj => { + console.log('Requester') + console.log(slack_obj) + + return send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + }) + + return bluebird.join( + promise_slack_to_supervisor, + promise_slack_to_requestor, + () => bluebird.resolve() + ) +} + +Slack.prototype.promise_leave_request_slacks = function(args) { + const self = this + const leave = args.leave + const send_slack = self.get_send_slack() + + let template_name_to_supervisor = 'leave_request_to_supervisor_slack' + let template_name_to_requestor = 'leave_request_to_requestor_slack' + + if (leave.get('user').is_auto_approve()) { + template_name_to_supervisor = + 'leave_request_to_supervisor_autoapprove_slack' + template_name_to_requestor = 'leave_request_to_requestor_autoapprove_slack' + } + + const promise_slack_to_supervisor = supervisor => + self + .promise_rendered_slack_template({ + template_name: template_name_to_supervisor, + context: { + leave, + approver: supervisor, + requester: leave.get('user'), + user: supervisor, + slack_username: supervisor.slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) + + const promise_slack_to_requestor = self + .promise_rendered_slack_template({ + template_name: template_name_to_requestor, + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver'), + slack_username: leave.get('user').slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) + + return bluebird.join( + promise_slack_to_requestor, + leave + .get('user') + .promise_supervisors() + .map(supervisor => promise_slack_to_supervisor(supervisor)), + () => bluebird.resolve() + ) +} + +Slack.prototype.promise_leave_request_decision_slacks = function(args) { + const self = this + const leave = args.leave + const action = args.action + const was_pended_revoke = args.was_pended_revoke + const send_slack = self.get_send_slack() + + const promise_slack_to_supervisor = self + .promise_rendered_slack_template({ + template_name: 'leave_request_decision_to_supervisor_slack', + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + action, + was_pended_revoke, + user: leave.get('approver'), + slack_username: leave.get('approver').slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) + + const promise_slack_to_requestor = self + .promise_rendered_slack_template({ + template_name: 'leave_request_decision_to_requestor_slack', + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + action, + was_pended_revoke, + user: leave.get('user'), + slack_username: leave.get('user').slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) + + return bluebird.join( + promise_slack_to_supervisor, + promise_slack_to_requestor, + () => bluebird.resolve() + ) +} + +Slack.prototype.promise_forgot_password_slack = function(args) { + const self = this + const user = args.user + const send_slack = self.get_send_slack() + + console.log('promise_forgot_password_slack') + console.log(args) + + return user + .getCompany() + .then(company => + self.promise_rendered_slack_template({ + template_name: 'forgot_password_slack', + context: { + user, + company, + slack_username: user.slack_username + } + }) + ) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) +} + +Slack.prototype.promise_reset_password_slack = function(args) { + const self = this + const user = args.user + const send_slack = self.get_send_slack() + + return self + .promise_rendered_slack_template({ + template_name: 'reset_password_slack', + context: { + user, + slack_username: user.slack_username + } + }) + .then(slack_obj => + send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + ) +} + +Slack.prototype.promise_leave_request_cancel_slacks = function(args) { + const self = this + const leave = args.leave + const send_slack = self.get_send_slack() + + const promise_slack_to_supervisor = self + .promise_rendered_slack_template({ + template_name: 'leave_request_cancel_to_supervisor_slack', + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('approver'), + slack_username: leave.get('approver').slack_username + } + }) + .then(slack_obj => { + console.log('APPROVER') + console.log(slack_obj) + return send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + }) + + const promise_slack_to_requestor = self + .promise_rendered_slack_template({ + template_name: 'leave_request_cancel_to_requestor_slack', + context: { + leave, + approver: leave.get('approver'), + requester: leave.get('user'), + user: leave.get('user'), + slack_username: leave.get('user').slack_username + } + }) + + .then(slack_obj => { + console.log('APPROVER') + console.log(slack_obj) + return send_slack({ + channel: slack_obj.slack_username, + text: slack_obj.text + }).then(send_result => bluebird.resolve(send_result)) + }) + + return bluebird.join( + promise_slack_to_supervisor, + promise_slack_to_requestor, + () => bluebird.resolve() + ) +} + +module.exports = Slack diff --git a/lib/util/index.js b/lib/util/index.js new file mode 100644 index 000000000..52fe8d01f --- /dev/null +++ b/lib/util/index.js @@ -0,0 +1,12 @@ +const config = require('../config') + +const defaultLocale = config.get('locale_code_for_sorting') || 'en' + +/** + * Local aware comparator to be used as compare function for Array's `sort` function + */ +const sorter = (a, b) => String(a).localeCompare(String(b), defaultLocale) + +module.exports = { + sorter +} diff --git a/lib/view/helpers.js b/lib/view/helpers.js index c6b27d694..4760c523f 100644 --- a/lib/view/helpers.js +++ b/lib/view/helpers.js @@ -1,95 +1,172 @@ +'use strict' -"use strict"; +const moment = require('moment') +const moment_tz = require('moment-timezone') +const config = require('../config') -var - moment = require('moment-timezone'), - config = require('../config'); +function as_date_formatted(date_str, format, options) { + if (!date_str) return '' -var as_date_formatted = function(date_str, format, options) { - - if (! date_str) return ''; + if (!format) { + format = get_current_format(options) + } - if ( ! format ) { - format = get_current_format( options ); + // Special case when we have to take time zone into consideration + // when printing date (usually it is needed for those dates recorded + // automatically as UTC time stampts) + if (options.tom_take_timezone_into_consideration) { + return moment_tz + .utc(date_str) + .tz(get_timezone(options)) + .format(format) } - return moment(date_str).format(format); + return moment.utc(date_str).format(format) } // Note: that currently it returns only truely value of given property, that is if there property // exists and its value is falsy, that undef is returned back -var get_property_from_options = function(options, property_name){ - var value; - - if ( options.hasOwnProperty('data') - && options.data.hasOwnProperty('root') - && options.data.root.hasOwnProperty(property_name) - && options.data.root[property_name] +function get_property_from_options(options, property_name) { + let value + + if ( + options.hasOwnProperty('data') && + options.data.hasOwnProperty('root') && + options.data.root.hasOwnProperty(property_name) && + options.data.root[property_name] ) { - value = options.data.root[property_name]; + value = options.data.root[property_name] } - return value; -}; + return value +} + +function get_current_format(options) { + let format = 'YYYY-MM-DD' + + const user = + get_property_from_options(options, 'logged_user') || + get_property_from_options(options, 'user') -var get_current_format = function(options){ + if (user && user.hasOwnProperty('company')) { + format = user.company.get_default_date_format() + } - var format = 'YYYY-MM-DD'; + return format +} - var user = get_property_from_options(options, 'logged_user') - || get_property_from_options(options, 'user'); +function get_timezone(options) { + let timezone = 'Europe/London' - if ( user && user.hasOwnProperty('company') ) { - format = user.company.get_default_date_format(); + const user = + get_property_from_options(options, 'logged_user') || + get_property_from_options(options, 'user') + + if (user && user.hasOwnProperty('company')) { + timezone = user.company.timezone } - return format; + return timezone } -module.exports = function(){ +module.exports = function() { return { // Handlebars does not allow to have conditions in IF, here is // workaround picked from here: http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional - if_equal : function(v1, v2, options){ - if(v1 === v2) { - return options.fn(this); + eq: function(a, b) { + return a === b + }, + if_equal: function(v1, v2, options) { + if (v1 === v2) { + return options.fn(this) } - return options.inverse(this); + return options.inverse(this) }, - // Given string with UTC date return string with date formated in customer specific manner - as_date : function(date_string, options) { + or: (a, b) => a || b, + not: a => !a, - return as_date_formatted(date_string, undefined, options); + // Given string with UTC date return string with date formated in customer specific manner + as_date: function(date_string, options) { + return as_date_formatted(date_string, undefined, options) }, // Return string with given date formated as a timestamp - as_datetime : function (date_str, options) { + as_datetime: function(date_str, options) { return as_date_formatted( date_str, get_current_format(options) + ' HH:mm:ss', options - ); + ) + }, + + // Do the same as "as_date" method but takes into consideration of company + // timezone. It is needed for rendering dates that were recorded in database + // automatically in UTC, dates that were recorded based on explicit input + // from users should not be used with this method (but with "as_date" instead) + as_date_from_timestamp: function(date_string, options) { + // Add custom flag to options so further down the call stack + // we would know that date needs to be corrected with + // current timezone + options.tom_take_timezone_into_consideration = true + + return as_date_formatted(date_string, undefined, options) + }, + + // Similar to "as_datetime" but with the same twist as "as_date_from_timestamp" + as_datetime_from_timestamp: function(date_str, options) { + // Add custom flag to options so further down the call stack + // we would know that date needs to be corrected with + // current timezone + options.tom_take_timezone_into_consideration = true + + return as_date_formatted( + date_str, + get_current_format(options) + ' HH:mm:ss', + options + ) }, // Given string with UTC date and string with moment.js format return corresponding string - as_date_formatted : as_date_formatted, + as_date_formatted, // Get access to config value holding application domain - get_application_domain : function() { - return config.get('application_domain'); + get_application_domain: function() { + return config.get('branding:url') }, // Return URL to the web site with promotion materials for TimeOff.Management - get_promotion_website_domain : function() { - return config.get('promotion_website_domain'); + get_promotion_website_domain: function() { + return config.get('branding:website') + }, + + concatenate: function() { + const arg = Array.prototype.slice.call(arguments, 0) + arg.pop() + return arg.join('') }, - concatenate : function() { - var arg = Array.prototype.slice.call(arguments,0); - arg.pop(); - return arg.join(''); + ga_tracker: config.get('google:analytics:tracker'), + // Should we include Google Analitics snipet? + // (based on application config) + is_ga_analitics_on: function(options) { + if (config.get('google:analytics:tracker')) { + return options.fn(this) + } + + return options.inverse(this) }, - }; -}; + is_force_to_explicitly_select_type_when_requesting_new_leave: function( + options + ) { + if ( + config.get('force_to_explicitly_select_type_when_requesting_new_leave') + ) { + return options.fn(this) + } + + return options.inverse(this) + } + } +} diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 000000000..024656cdd --- /dev/null +++ b/locales/en.json @@ -0,0 +1,5 @@ +{ + "welcome_back": "Welcome back", + "greeting": "Hello", + "calendar_for": "calendar for" +} \ No newline at end of file diff --git a/locales/es.json b/locales/es.json new file mode 100644 index 000000000..556af00a3 --- /dev/null +++ b/locales/es.json @@ -0,0 +1,5 @@ +{ + "welcome_back": "Bienvenido de nuevo", + "greeting": "Hola", + "calendar_for": "calendario para" +} \ No newline at end of file diff --git a/managed_context/metadata.json b/managed_context/metadata.json new file mode 100644 index 000000000..2e0910c4a --- /dev/null +++ b/managed_context/metadata.json @@ -0,0 +1 @@ +{"current_schema_version":"0.0.1"} \ No newline at end of file diff --git a/migrations/20170103144054-add_default_date_format.js b/migrations/20170103144054-add_default_date_format.js index f48da0c09..fa7369e27 100644 --- a/migrations/20170103144054-add_default_date_format.js +++ b/migrations/20170103144054-add_default_date_format.js @@ -1,27 +1,21 @@ -'use strict'; +'use strict' module.exports = { - up: function (queryInterface, Sequelize) { - - queryInterface.describeTable('Companies').then(function(attributes){ - + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('companies').then(function(attributes) { if (attributes.hasOwnProperty('date_format')) { - return 1; + return 1 } - return queryInterface.addColumn( - 'Companies', - 'date_format', - { - type : Sequelize.STRING, - allowNull : false, - defaultValue : 'YYYY-MM-DD', - } - ); - }); + return queryInterface.addColumn('companies', 'date_format', { + type: Sequelize.STRING, + allowNull: false, + defaultValue: 'YYYY-MM-DD' + }) + }) }, - down: function (queryInterface, Sequelize) { - return queryInterface.removeColumn('Companies', 'date_format'); + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('companies', 'date_format') } -}; +} diff --git a/migrations/20170329060832-rename_allowence_to_allowance.js b/migrations/20170329060832-rename_allowence_to_allowance.js index ed040078e..ccff8b207 100644 --- a/migrations/20170329060832-rename_allowence_to_allowance.js +++ b/migrations/20170329060832-rename_allowence_to_allowance.js @@ -1,65 +1,77 @@ -'use strict'; +'use strict' -var models = require('../lib/model/db'); +const models = require('../lib/model/db') module.exports = { - up: function (queryInterface, Sequelize) { - - queryInterface.describeTable('Departments').then(function(attributes){ - - if (attributes.hasOwnProperty('allowance')) { - return 1; - } - - if ('sqlite' === queryInterface.sequelize.getDialect()) { - - console.log('Going into SQLIite case'); - - return queryInterface - // Create Temp Departments based on current model definitiom - .createTable('Departments_backup', models.Department.attributes) - - .then(function(){ - return queryInterface.sequelize.query('PRAGMA foreign_keys=off;'); - }) - - // Copy data form original Departments into new Temp one - .then(function(){ - return queryInterface.sequelize.query( - 'INSERT INTO `Departments_backup` (id, name, include_public_holidays, createdAt, updatedAt, companyId, bossId, allowance) SELECT id, name, include_public_holidays, createdAt, updatedAt, companyId, bossId, allowence FROM `'+ models.Department.tableName +'`'); - }) - - .then(function(){ - return queryInterface.dropTable( models.Department.tableName ); - }) - - .then(function(){ - return queryInterface.renameTable('Departments_backup', models.Department.tableName); - }) - - .then(function(){ - return queryInterface.sequelize.query('PRAGMA foreign_keys=on;'); - }) - - .then(function(){ - queryInterface.addIndex(models.Department.tableName, ['companyId']); - }) - - .then(function(){ - queryInterface.addIndex(models.Department.tableName, ['id']); - }); - - } else { - - console.log('Generic option'); - - return queryInterface.renameColumn('Departments', 'allowence', 'allowance') - .then(function(d){ console.dir(d) }); - } - }); + up: function(queryInterface, Sequelize) { + return queryInterface + .describeTable('departments') + .then(function(attributes) { + if (attributes.hasOwnProperty('allowance')) { + return 1 + } + + if (queryInterface.sequelize.getDialect() === 'sqlite') { + console.log('Going into SQLIite case') + + return ( + queryInterface + // Create Temp Departments based on current model definitiom + .createTable('Departments_backup', models.Department.attributes) + + .then(function() { + return queryInterface.sequelize.query( + 'PRAGMA foreign_keys=off;' + ) + }) + + // Copy data form original Departments into new Temp one + .then(function() { + return queryInterface.sequelize.query( + 'INSERT INTO `Departments_backup` (id, name, include_public_holidays, createdAt, updatedAt, company_id, bossId, allowance) SELECT id, name, include_public_holidays, createdAt, updatedAt, company_id, bossId, allowence FROM `' + + models.Department.tableName + + '`' + ) + }) + + .then(function() { + return queryInterface.dropTable(models.Department.tableName) + }) + + .then(function() { + return queryInterface.renameTable( + 'Departments_backup', + models.Department.tableName + ) + }) + + .then(function() { + return queryInterface.sequelize.query('PRAGMA foreign_keys=on;') + }) + + .then(function() { + queryInterface.addIndex(models.Department.tableName, [ + 'company_id' + ]) + }) + + .then(function() { + queryInterface.addIndex(models.Department.tableName, ['id']) + }) + ) + } else { + console.log('Generic option') + + return queryInterface + .renameColumn('departments', 'allowence', 'allowance') + .then(function(d) { + console.dir(d) + }) + } + }) }, - down: function (queryInterface, Sequelize) { - return queryInterface.renameColumn('Departments', 'allowance', 'allowence'); + down: function(queryInterface, Sequelize) { + return queryInterface.renameColumn('departments', 'allowance', 'allowence') } -}; +} diff --git a/migrations/20170530-add_auto_approve_flag_to_user.js b/migrations/20170530-add_auto_approve_flag_to_user.js index f3d63691d..cd335e16c 100644 --- a/migrations/20170530-add_auto_approve_flag_to_user.js +++ b/migrations/20170530-add_auto_approve_flag_to_user.js @@ -1,27 +1,23 @@ +'use strict' -'use strict'; - -var models = require('../lib/model/db'); +const models = require('../lib/model/db') module.exports = { - up: function (queryInterface, Sequelize) { - - queryInterface.describeTable('Users').then(function(attributes){ - + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('users').then(function(attributes) { if (attributes.hasOwnProperty('auto_approve')) { - return 1; + return 1 } return queryInterface.addColumn( - 'Users', + 'users', 'auto_approve', models.User.attributes.auto_approve - ); - }); - + ) + }) }, - down: function (queryInterface, Sequelize) { - return queryInterface.removeColumn('Users', 'auto_approve'); + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('users', 'auto_approve') } -}; +} diff --git a/migrations/20171218-company-wide-message.js b/migrations/20171218-company-wide-message.js new file mode 100644 index 000000000..13da56c66 --- /dev/null +++ b/migrations/20171218-company-wide-message.js @@ -0,0 +1,23 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('companies').then(function(attributes) { + if (attributes.hasOwnProperty('company_wide_message')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'company_wide_message', + models.Company.attributes.company_wide_message + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('companies', 'company_wide_message') + } +} diff --git a/migrations/20171219-allowance-adjustment-per-year.js b/migrations/20171219-allowance-adjustment-per-year.js new file mode 100644 index 000000000..94308d577 --- /dev/null +++ b/migrations/20171219-allowance-adjustment-per-year.js @@ -0,0 +1,34 @@ +'use strict' + +const models = require('../lib/model/db') +const Promise = require('bluebird') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface + .createTable( + models.UserAllowanceAdjustment.tableName, + models.UserAllowanceAdjustment.attributes + ) + .then(() => queryInterface.describeTable('users')) + .then(function(attributes) { + if (!attributes.hasOwnProperty('adjustment')) { + return Promise.resolve() + } + + const sql = + 'INSERT INTO user_allowance_adjustment (year, adjustment, user_id, created_at) ' + + "SELECT 2017 AS year, adjustment as adjustment, id as user_id, date() || ' ' || time() as created_at " + + 'FROM users' + + return queryInterface.sequelize.query(sql) + }) + + .then(() => Promise.resolve()) + }, + + down: function(queryInterface, Sequelize) { + // No way back! + return Promise.resolve() + } +} diff --git a/migrations/20171220-drop-adjustment-column-from-user.js b/migrations/20171220-drop-adjustment-column-from-user.js new file mode 100644 index 000000000..8902c971b --- /dev/null +++ b/migrations/20171220-drop-adjustment-column-from-user.js @@ -0,0 +1,54 @@ +'use strict' + +const models = require('../lib/model/db') +const Promise = require('bluebird') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('users').then(attributes => { + if (!attributes.hasOwnProperty('adjustment')) { + return Promise.resolve() + } + + if (queryInterface.sequelize.getDialect() !== 'sqlite') { + // For non SQLite: it is easy + return queryInterface.removeColumn(models.User.tableName, 'adjustment') + } + + // For SQLite it is "fun" + + return ( + queryInterface + // Create Temp Users based on current model definitiom + .createTable('Users_backup', models.User.attributes) + + .then(function() { + return queryInterface.sequelize.query('PRAGMA foreign_keys=off;') + }) + + // Copy data form original Users into new Temp one + .then(function() { + return queryInterface.sequelize.query( + 'INSERT INTO `Users_backup` (`id`, `email`, `password`, `name`, `lastname`, `activated`, `admin`, `start_date`, `end_date`, `createdAt`, `updatedAt`, `company_id`, `department_id`, `auto_approve`) SELECT `id`, `email`, `password`, `name`, `lastname`, `activated`, `admin`, `start_date`, `end_date`, `createdAt`, `updatedAt`, `company_id`, `department_id`, `auto_approve` FROM `' + + models.User.tableName + + '`' + ) + }) + + .then(() => queryInterface.dropTable(models.User.tableName)) + .then(() => + queryInterface.renameTable('Users_backup', models.User.tableName) + ) + .then(() => queryInterface.sequelize.query('PRAGMA foreign_keys=on;')) + .then(() => + queryInterface.addIndex(models.User.tableName, ['company_id']) + ) + ) + }) + }, + + down: function(queryInterface, Sequelize) { + // No way back! + return Promise.resolve() + } +} diff --git a/migrations/20171222-add-carry-over-allowance-column.js b/migrations/20171222-add-carry-over-allowance-column.js new file mode 100644 index 000000000..048ecd1fb --- /dev/null +++ b/migrations/20171222-add-carry-over-allowance-column.js @@ -0,0 +1,28 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface + .describeTable('user_allowance_adjustment') + .then(function(attributes) { + if (attributes.hasOwnProperty('carried_over_allowance')) { + return 1 + } + + return queryInterface.addColumn( + 'user_allowance_adjustment', + 'carried_over_allowance', + models.UserAllowanceAdjustment.attributes.carried_over_allowance + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn( + 'user_allowance_adjustment', + 'carried_over_allowance' + ) + } +} diff --git a/migrations/20171228-add-time-zone-field-to-company.js b/migrations/20171228-add-time-zone-field-to-company.js new file mode 100644 index 000000000..c07a0ca0c --- /dev/null +++ b/migrations/20171228-add-time-zone-field-to-company.js @@ -0,0 +1,23 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('companies').then(function(attributes) { + if (attributes.hasOwnProperty('timezone')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'timezone', + models.Company.attributes.timezone + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('companies', 'timezone') + } +} diff --git a/migrations/20180102-company-mode.js b/migrations/20180102-company-mode.js new file mode 100644 index 000000000..ebde683ae --- /dev/null +++ b/migrations/20180102-company-mode.js @@ -0,0 +1,23 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('companies').then(function(attributes) { + if (attributes.hasOwnProperty('mode')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'mode', + models.Company.attributes.mode + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('companies', 'mode') + } +} diff --git a/migrations/20180103-sort-order-for-leave-types.js b/migrations/20180103-sort-order-for-leave-types.js new file mode 100644 index 000000000..3a12151b5 --- /dev/null +++ b/migrations/20180103-sort-order-for-leave-types.js @@ -0,0 +1,25 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface + .describeTable('leave_types') + .then(function(attributes) { + if (attributes.hasOwnProperty('sort_order')) { + return 1 + } + + return queryInterface.addColumn( + 'leave_types', + 'sort_order', + models.LeaveType.attributes.sort_order + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('leave_types', 'sort_order') + } +} diff --git a/migrations/20180427-add-is_accrued_allowance-to-department.js b/migrations/20180427-add-is_accrued_allowance-to-department.js new file mode 100644 index 000000000..c39094310 --- /dev/null +++ b/migrations/20180427-add-is_accrued_allowance-to-department.js @@ -0,0 +1,25 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface + .describeTable('departments') + .then(function(attributes) { + if (attributes.hasOwnProperty('is_accrued_allowance')) { + return 1 + } + + return queryInterface.addColumn( + 'departments', + 'is_accrued_allowance', + models.Department.attributes.is_accrued_allowance + ) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('departments', 'is_accrued_allowance') + } +} diff --git a/migrations/20181119-integration-api-token-per-company.js b/migrations/20181119-integration-api-token-per-company.js new file mode 100644 index 000000000..612e5f008 --- /dev/null +++ b/migrations/20181119-integration-api-token-per-company.js @@ -0,0 +1,40 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: (queryInterface, Sequelize) => + queryInterface + .describeTable('companies') + .then(attributes => { + if (attributes.hasOwnProperty('integration_api_token')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'integration_api_token', + models.Company.attributes.integration_api_token + ) + }) + .then(() => + queryInterface.describeTable('companies').then(attributes => { + if (attributes.hasOwnProperty('integration_api_enabled')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'integration_api_enabled', + models.Company.attributes.integration_api_enabled + ) + }) + ), + + down: (queryInterface, Sequelize) => + queryInterface + .removeColumn('companies', 'integration_api_token') + .then(() => + queryInterface.removeColumn('companies', 'integration_api_enabled') + ) +} diff --git a/migrations/20181213-carry-over-conf.js b/migrations/20181213-carry-over-conf.js new file mode 100644 index 000000000..d98e5bcb6 --- /dev/null +++ b/migrations/20181213-carry-over-conf.js @@ -0,0 +1,22 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: (queryInterface, Sequelize) => + queryInterface.describeTable('companies').then(attributes => { + if (attributes.hasOwnProperty('carry_over')) { + return 1 + } + + return queryInterface.addColumn( + 'companies', + 'carry_over', + models.Company.attributes.carry_over + ) + }), + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('companies', 'carry_over') + } +} diff --git a/migrations/20190118-change-type-value-for-api-token.js b/migrations/20190118-change-type-value-for-api-token.js new file mode 100644 index 000000000..24a833dd4 --- /dev/null +++ b/migrations/20190118-change-type-value-for-api-token.js @@ -0,0 +1,43 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.describeTable('companies').then(attributes => { + if (attributes.integration_api_token.type === 'UUID') { + return 1 + } + + return ( + queryInterface + // Create Temp Compaies based on current model definitiom + .createTable('Companies_backup', models.Company.attributes) + .then(() => + queryInterface.sequelize.query('PRAGMA foreign_keys=off;') + ) + .then(() => + queryInterface.sequelize.query( + 'INSERT INTO `Companies_backup` (`id`,`name`,`country`,`start_of_new_year`,`createdAt`,`updatedAt`,share_all_absences,ldap_auth_enabled,ldap_auth_config,`date_format`,`company_wide_message`,`mode`,`timezone`,`integration_api_token`,`integration_api_enabled`,`carry_over`) SELECT `id`,`name`,`country`,`start_of_new_year`,`createdAt`,`updatedAt`,share_all_absences,ldap_auth_enabled,ldap_auth_config,`date_format`,`company_wide_message`,`mode`,`timezone`,`integration_api_token`,`integration_api_enabled`,`carry_over` FROM `' + + models.Company.tableName + + '`' + ) + ) + .then(() => queryInterface.dropTable(models.Company.tableName)) + .then(() => + queryInterface.renameTable( + 'Companies_backup', + models.Company.tableName + ) + ) + .then(() => queryInterface.sequelize.query('PRAGMA foreign_keys=on;')) + .then(() => queryInterface.addIndex(models.Company.tableName, ['id'])) + ) + }) + }, + + down: function(queryInterface, Sequelize) { + // No way back! + return Promise.resolve() + } +} diff --git a/migrations/20190502-entity-types.js b/migrations/20190502-entity-types.js new file mode 100644 index 000000000..7cf5f1824 --- /dev/null +++ b/migrations/20190502-entity-types.js @@ -0,0 +1,232 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: async function(queryInterface, Sequelize) { + await queryInterface.describeTable('comments').then(async attributes => { + if (attributes.entityType) { + await queryInterface.renameColumn( + 'comment', + 'entityType', + 'entity_type' + ) + } + + if (attributes.entityId) { + await queryInterface.renameColumn('comment', 'entityId', 'entity_id') + } + + if (attributes.companyId) { + await queryInterface.renameColumn('comment', 'companyId', 'company_id') + } + }) + + await queryInterface.describeTable('audit').then(async attributes => { + if (attributes.entityType) { + await queryInterface.renameColumn('audit', 'entityType', 'entity_type') + } + + if (attributes.entityId) { + await queryInterface.renameColumn('audit', 'entityId', 'entity_id') + } + + if (attributes.oldValue) { + await queryInterface.renameColumn('audit', 'oldValue', 'old_value') + } + + if (attributes.newValue) { + await queryInterface.renameColumn('audit', 'newValue', 'new_value') + } + }) + + await queryInterface.describeTable('companies').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'companies', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'companies', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface.describeTable('departments').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'departments', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'department', + 'DepartmentId', + 'department_id' + ) + } + + if (attributes.bossId) { + await queryInterface.renameColumn('departments', 'bossId', 'manager_id') + } + }) + + await queryInterface + .describeTable('bank_holidays') + .then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'bank_holidays', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'bank_holidays', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface + .describeTable('department_supervisors') + .then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'department_supervisors', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'department_supervisors', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface + .describeTable('email_audits') + .then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'email_audits', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'email_audits', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface.describeTable('leaves').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn('leaves', 'companyId', 'company_id') + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'leaves', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface.describeTable('leave_types').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'leave_types', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'leave_types', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface.describeTable('schedules').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'schedules', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'schedules', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface.describeTable('users').then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn('users', 'companyId', 'company_id') + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'users', + 'DepartmentId', + 'department_id' + ) + } + }) + + await queryInterface + .describeTable('user_allowance_adjustment') + .then(async attributes => { + if (attributes.companyId) { + await queryInterface.renameColumn( + 'user_allowance_adjustment', + 'companyId', + 'company_id' + ) + } + + if (attributes.DepartmentId) { + await queryInterface.renameColumn( + 'user_allowance_adjustment', + 'DepartmentId', + 'department_id' + ) + } + }) + }, + + down: function(queryInterface, Sequelize) { + // No way back! + return Promise.resolve() + } +} diff --git a/migrations/20190629-add-is_team_view_hidden_field.js b/migrations/20190629-add-is_team_view_hidden_field.js new file mode 100644 index 000000000..e460343eb --- /dev/null +++ b/migrations/20190629-add-is_team_view_hidden_field.js @@ -0,0 +1,16 @@ +module.exports = { + up: (queryInterface, Sequelize) => queryInterface.describeTable('companies').then(attributes => { + if (attributes.hasOwnProperty('is_team_view_hidden')) { + return Promise.resolve() + } + + return queryInterface.addColumn( + 'companies', + 'is_team_view_hidden', + models.Company.attributes.is_team_view_hidden + ) + }), + + down: (queryInterface, Sequelize) => + queryInterface.removeColumn('companies', 'is_team_view_hidden') +} diff --git a/migrations/20191030-compress-email-audit.js b/migrations/20191030-compress-email-audit.js new file mode 100644 index 000000000..a995b37e0 --- /dev/null +++ b/migrations/20191030-compress-email-audit.js @@ -0,0 +1,14 @@ +const models = require('../lib/model/db') + +module.exports = { + up: () => + models.EmailAudit.findAll() + .map(rec => rec.update({ body: htmlToText.fromString(rec.body) }), { + concurrency: 1 + }) + .then(() => console.log('Done!')) + .catch(err => console.error('Error updating records:', err)), + + // Do nothing + down: () => Promise.resolve() +} diff --git a/migrations/20200204185448-add_auto_approve_flag_to_leave_type.js b/migrations/20200204185448-add_auto_approve_flag_to_leave_type.js new file mode 100644 index 000000000..2d4c0d627 --- /dev/null +++ b/migrations/20200204185448-add_auto_approve_flag_to_leave_type.js @@ -0,0 +1,25 @@ +'use strict' + +const models = require('../lib/model/db') + +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface + .describeTable('leave_types') + .then(function(attributes) { + if (attributes.hasOwnProperty('auto_approve')) { + return Promise.resolve() + } + + return queryInterface.addColumn('leave_types', 'auto_approve', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false + }) + }) + }, + + down: function(queryInterface, Sequelize) { + return queryInterface.removeColumn('leave_types', 'auto_approve') + } +} diff --git a/migrations/20240405173944-update-cols-allowance-int-to-float.js b/migrations/20240405173944-update-cols-allowance-int-to-float.js new file mode 100644 index 000000000..a38a5cfc1 --- /dev/null +++ b/migrations/20240405173944-update-cols-allowance-int-to-float.js @@ -0,0 +1,30 @@ +'use strict' + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.changeColumn('departments', 'allowance', { + type: Sequelize.FLOAT, + allowNull: false, + defaultValue: 20 + }) + await queryInterface.changeColumn('departments', 'personal', { + type: Sequelize.FLOAT, + allowNull: false, + defaultValue: 5 + }) + }, + + async down(queryInterface, Sequelize) { + await queryInterface.changeColumn('departments', 'allowance', { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 20 + }) + await queryInterface.changeColumn('departments', 'personal', { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 5 + }) + } +} diff --git a/migrations/20240612153328-add-auto-approve-and-manager-only-to-leave-type.js b/migrations/20240612153328-add-auto-approve-and-manager-only-to-leave-type.js new file mode 100644 index 000000000..602ff43e0 --- /dev/null +++ b/migrations/20240612153328-add-auto-approve-and-manager-only-to-leave-type.js @@ -0,0 +1,17 @@ +'use strict' + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('leave_types', 'manager_only', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: + 'If true, this leave type can only be used by managers/supervisors' + }) + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.query.removeColumn('leave_types', 'manager_only') + } +} diff --git a/migrations/20240703-private-leave-types.js b/migrations/20240703-private-leave-types.js new file mode 100644 index 000000000..f389fd927 --- /dev/null +++ b/migrations/20240703-private-leave-types.js @@ -0,0 +1,17 @@ +'use strict' + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('leave_types', 'private_leave', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: + 'If true, this leave type is private and only visible to the employee or a manager' + }) + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.query.removeColumn('leave_types', 'private_leave') + } +} diff --git a/models/index.js b/models/index.js new file mode 100644 index 000000000..0d82c797c --- /dev/null +++ b/models/index.js @@ -0,0 +1,48 @@ +'use strict' + +const fs = require('fs') +const path = require('path') +const Sequelize = require('sequelize') +const process = require('process') +const basename = path.basename(__filename) +const env = process.env.NODE_ENV || 'development' +const config = require(__dirname + '/../config/config.json')[env] +const db = {} + +let sequelize +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config) +} else { + sequelize = new Sequelize( + config.database, + config.username, + config.password, + config + ) +} + +fs.readdirSync(__dirname) + .filter(file => ( + file.indexOf('.') !== 0 && + file !== basename && + file.slice(-3) === '.js' && + file.indexOf('.test.js') === -1 + )) + .forEach(file => { + const model = require(path.join(__dirname, file))( + sequelize, + Sequelize.DataTypes + ) + db[model.name] = model + }) + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db) + } +}) + +db.sequelize = sequelize +db.Sequelize = Sequelize + +module.exports = db diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..085fa8010 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,9912 @@ +{ + "name": "TimeOff.Management", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "TimeOff.Management", + "version": "1.0.0", + "hasInstallScript": true, + "dependencies": { + "passport-local": "^1.0.0", + "joi": "~14.3.1", + "html-to-text": "^5.1.1", + "connect-session-sequelize": "6.0.0", + "express-handlebars": "^3.0.2", + "dotenv": "^16.4.5", + "serve-favicon": "^2.5.0", + "ical-generator": "^1.7.1", + "passport-http-bearer": "^1.0.1", + "body-parser": "^1.19.0", + "nodemailer": "^6.1.1", + "csv": "~5.1.1", + "ldapauth-fork": "^4.2.0", + "cookie-parser": "^1.4.4", + "nodemailer-smtp-transport": "^2.7.4", + "optimist": "^0.6.1", + "underscore": "^1.9.1", + "moment": "^2.24.0", + "uuid": "^3.3.2", + "@handlebars/allow-prototype-access": "^1.0.5", + "morgan": "^1.9.1", + "bluebird": "^3.5.4", + "debug": "~4.1.1", + "sequelize-cli": "^5.5.1", + "passport": "^0.4.0", + "nconf": "^0.10.0", + "formidable": "~1.2.1", + "passport-google-oauth20": "^1.0.0", + "@slack/client": "^4.1.0", + "validator": "^13.11.0", + "node-uuid": "^1.4.8", + "express": "^4.16.4", + "multer": "^1.4.1", + "express-session": "^1.16.1", + "connect-redis": "^6.0.0", + "sequelize": "^5.22.5", + "moment-timezone": "^0.5.25" + }, + "devDependencies": { + "chai": "^4.2.0", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-mocha": "^10.2.0", + "eslint-plugin-n": "^16.2.0", + "eslint-plugin-promise": "^6.1.1", + "i18n": "^0.15.1", + "mocha": "^6.2.2", + "node-sass": "^8.0.0", + "prettier": "1.17.0", + "request-promise": "^4.2.4", + "selenium-webdriver": "^3.6.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "mysql2": "^3.9.1", + "pg": "^8.11.4", + "redis": "^3.1.2", + "sqlite3": "^5.1.6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "optional": true, + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/html-to-text": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-5.1.1.tgz", + "integrity": "sha512-Bci6bD/JIfZSvG4s0gW/9mMKwBRoe/1RWLxUME/d6WUSZCdY7T60bssf/jFf7EYXRyqU4P5xdClVqiYU0/ypdA==", + "dependencies": { + "he": "^1.2.0", + "htmlparser2": "^3.10.1", + "lodash": "^4.17.11", + "minimist": "^1.2.0" + }, + "bin": { + "html-to-text": "bin/cli.js" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "devOptional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/sass-graph/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nodemailer-smtp-transport": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.4.tgz", + "integrity": "sha512-1e86YhJ633OZWk3OHWS5TpuoYXG/LtY2/RzNiB5+EkFifDdqHCNHBnExd5cobx0ZSHJLNGM8EKnDuHRFIjFi6Q==", + "dependencies": { + "nodemailer-shared": "1.1.0", + "nodemailer-wellknown": "0.1.10", + "smtp-connection": "2.12.0" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "devOptional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", + "dev": true, + "dependencies": { + "moo": "^0.5.1" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/node-sass": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-8.0.0.tgz", + "integrity": "sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "async-foreach": "^0.1.3", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "make-fetch-happen": "^10.0.4", + "meow": "^9.0.0", + "nan": "^2.17.0", + "node-gyp": "^8.4.1", + "sass-graph": "^4.0.1", + "stdout-stream": "^1.4.0", + "true-case-path": "^2.2.1" + }, + "bin": { + "node-sass": "bin/node-sass" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/follow-redirects/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/editorconfig/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "peer": true + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wkx": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz", + "integrity": "sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "devOptional": true, + "dependencies": { + "minipass-flush": "^1.0.5", + "minipass-fetch": "^1.3.2", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "http-cache-semantics": "^4.1.0", + "ssri": "^8.0.0", + "socks-proxy-agent": "^6.0.0", + "cacache": "^15.2.0", + "https-proxy-agent": "^5.0.0", + "promise-retry": "^2.0.1", + "minipass-collect": "^1.0.2", + "http-proxy-agent": "^4.0.1", + "agentkeepalive": "^4.1.3", + "negotiator": "^0.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-color": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", + "dependencies": { + "ansi-regex": "^2.1.1", + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nconf/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/wrap-ansi/node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/nconf/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "devOptional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/dicer/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "semver": "^6.3.1", + "doctrine": "^2.1.0", + "object.values": "^1.1.7", + "is-glob": "^4.0.3", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "array.prototype.flat": "^1.3.2", + "tsconfig-paths": "^3.15.0", + "eslint-import-resolver-node": "^0.3.9", + "object.groupby": "^1.0.1", + "array.prototype.flatmap": "^1.3.2", + "object.fromentries": "^2.0.7", + "debug": "^3.2.7", + "is-core-module": "^2.13.1", + "minimatch": "^3.1.2", + "array.prototype.findlastindex": "^1.2.3", + "array-includes": "^3.1.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg": { + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "optional": true, + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@slack/client": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@slack/client/-/client-4.12.0.tgz", + "integrity": "sha512-ltbdkcIWk2eIptCCT/oPmeCGlG8xb3kXfwuPTtvNujioLMo2xXqiPdfl7xK+AeUfnvj3fJLYbpTPuBTscuhgzw==", + "deprecated": "Slack Client is deprecated - Use @slack/web-api, @slack/rtm-api, or @slack/webhook instead.", + "dependencies": { + "retry": "^0.12.0", + "@types/p-cancelable": "^1.0.0", + "p-retry": "^3.0.1", + "object.values": "^1.1.0", + "axios": "^0.18.0", + "@types/retry": "^0.12.0", + "@types/is-stream": "^1.1.0", + "@types/p-queue": "^2.3.2", + "is-stream": "^1.1.0", + "@types/node": ">=6.0.0", + "finity": "^0.5.4", + "@types/form-data": "^2.2.1", + "p-queue": "^2.4.2", + "object.entries": "^1.1.0", + "p-cancelable": "~1.0.0", + "eventemitter3": "^3.1.0", + "@types/p-retry": "^3.0.0", + "@types/ws": "^5.1.1", + "object.getownpropertydescriptors": "^2.0.3", + "ws": "^5.2.0", + "form-data": "^2.3.3" + }, + "engines": { + "node": ">= 6.9.0", + "npm": ">= 3.10.8" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "ignore": "^5.2.0", + "eslint-scope": "^7.2.2", + "js-yaml": "^4.1.0", + "natural-compare": "^1.4.0", + "doctrine": "^3.0.0", + "file-entry-cache": "^6.0.1", + "is-glob": "^4.0.0", + "lodash.merge": "^4.6.2", + "eslint-visitor-keys": "^3.4.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0", + "espree": "^9.6.1", + "@ungap/structured-clone": "^1.2.0", + "imurmurhash": "^0.1.4", + "cross-spawn": "^7.0.2", + "@eslint/eslintrc": "^2.1.4", + "graphemer": "^1.4.0", + "is-path-inside": "^3.0.3", + "@nodelib/fs.walk": "^1.2.8", + "@eslint-community/regexpp": "^4.6.1", + "@humanwhocodes/module-importer": "^1.0.1", + "@eslint-community/eslint-utils": "^4.2.0", + "chalk": "^4.0.0", + "debug": "^4.3.2", + "ajv": "^6.12.4", + "@humanwhocodes/config-array": "^0.11.14", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "esutils": "^2.0.2", + "globals": "^13.19.0", + "minimatch": "^3.1.2", + "glob-parent": "^6.0.2", + "fast-deep-equal": "^3.1.3", + "esquery": "^1.4.2", + "find-up": "^5.0.0", + "optionator": "^0.9.3", + "escape-string-regexp": "^4.0.0", + "@eslint/js": "8.57.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nconf/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serve-favicon": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==", + "dependencies": { + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/sequelize-pool": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-2.3.0.tgz", + "integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "devOptional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/is-bluebird": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", + "integrity": "sha512-PDRu1vVip5dGQg5tfn2qVCCyxbBYu5MhYUJwSfL/RoGBI97n1fxvilVazxzptZW0gcmsMH17H4EVZZI5E/RSeA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", + "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.10.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "devOptional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/selenium-webdriver/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/joi": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", + "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", + "deprecated": "This module has moved and is now available at @hapi/joi. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", + "dependencies": { + "hoek": "6.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "optional": true, + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist/node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "optional": true, + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/prettier": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nconf/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", + "deprecated": "Use uuid module instead", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "peer": true, + "dependencies": { + "isstream": "~0.1.2", + "oauth-sign": "~0.9.0", + "safe-buffer": "^5.1.2", + "is-typedarray": "~1.0.0", + "json-stringify-safe": "~5.0.1", + "performance-now": "^2.1.0", + "http-signature": "~1.2.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2", + "har-validator": "~5.1.3", + "extend": "~3.0.2", + "mime-types": "~2.1.19", + "tough-cookie": "~2.5.0", + "aws-sign2": "~0.7.0", + "caseless": "~0.12.0", + "aws4": "^1.8.0", + "forever-agent": "~0.6.1", + "combined-stream": "~1.0.6", + "form-data": "~2.3.2", + "qs": "~6.5.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/serve-favicon/node_modules/safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "node_modules/sass-graph/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/finity": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/finity/-/finity-0.5.4.tgz", + "integrity": "sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/stdout-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/ldapjs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", + "integrity": "sha512-Y8fRYFXOWWKBs9I2QRNXb0FncDcG0Gjc7YU6XI3kIV9hLMRaFKH5QHi2TZeB7FrpgXuabOgL0GdYALbssHxH5Q==", + "dependencies": { + "asn1": "0.2.3", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "bunyan": "^1.8.3", + "dashdash": "^1.14.0", + "ldap-filter": "0.2.2", + "once": "^1.4.0", + "vasync": "^1.6.4", + "verror": "^1.8.1" + }, + "bin": { + "ldapjs-add": "bin/ldapjs-add", + "ldapjs-compare": "bin/ldapjs-compare", + "ldapjs-delete": "bin/ldapjs-delete", + "ldapjs-modify": "bin/ldapjs-modify", + "ldapjs-search": "bin/ldapjs-search" + }, + "engines": { + "node": ">=0.10" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nodemailer-wellknown": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", + "integrity": "sha512-/VV4mjAEjfm2fn0loUvrpjvugw5rgurNjPO4WU24CuVSoeumsyLOTgaEWG8WoGdPxh1biOAp5JxDoy1hlA2zsw==" + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "devOptional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "devOptional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dependencies": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@types/retry": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", + "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==" + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stdout-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/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==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/httpntlm": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", + "integrity": "sha512-Tcz3Ct9efvNqw3QdTl3h6IgRRlIQxwKkJELN/aAIGnzi2xvb3pDHdnMs8BrxWLV6OoT4DlVyhzSVhFt/tk0lIw==", + "dependencies": { + "httpreq": ">=0.4.22", + "underscore": "~1.7.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/csv-stringify": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", + "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "optional": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ldap-filter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha512-HDnDRNY/z0E3qljSjDWtu7xXCUdiXzwadz7m1jIwl3XHhPMrqUyurOd32YWH5IZ3zZMP4PrG7gKdRIB2uZHKGA==", + "dependencies": { + "assert-plus": "0.1.5" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "peer": true + }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/express-handlebars": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.1.0.tgz", + "integrity": "sha512-7QlaXnSREMmN5P2o4gmpUZDfJlLtfBka9d6r7/ccXaU7rPp76odw9YYtwZYdIiha2JqwiaG6o2Wu6NZJQ0u7Fg==", + "dependencies": { + "glob": "^7.1.3", + "graceful-fs": "^4.1.2", + "handlebars": "^4.1.2", + "object.assign": "^4.1.0", + "promise": "^8.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "peer": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==", + "dependencies": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "optional": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/node-gyp/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "devOptional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/nodemailer-fetch": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", + "integrity": "sha512-P7S5CEVGAmDrrpn351aXOLYs1R/7fD5NamfMCHyi6WIkbjS2eeZUB/TkuvpOQr0bvRZicVqo59+8wbhR3yrJbQ==" + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/csv-generate": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.4.3.tgz", + "integrity": "sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==" + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "dev": true + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/smtp-connection": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", + "integrity": "sha512-UP5jK4s5SGcUcqPN4U9ingqKt9mXYSKa52YhqxPuMecAnUOsVJpOmtgGaOm1urUBJZlzDt1M9WhZZkgbhxQlvg==", + "dependencies": { + "httpntlm": "1.6.1", + "nodemailer-shared": "1.1.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "devOptional": true + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "devOptional": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "devOptional": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/globule/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@messageformat/core": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.3.0.tgz", + "integrity": "sha512-YcXd3remTDdeMxAlbvW6oV9d/01/DZ8DHUFwSttO3LMzIZj3iO0NRw+u1xlsNNORFI+u0EQzD52ZX3+Udi0T3g==", + "dev": true, + "dependencies": { + "@messageformat/date-skeleton": "^1.0.0", + "@messageformat/number-skeleton": "^1.0.0", + "@messageformat/parser": "^5.1.0", + "@messageformat/runtime": "^3.0.1", + "make-plural": "^7.0.0", + "safe-identifier": "^0.4.1" + } + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "optional": true, + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/oauth": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz", + "integrity": "sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==" + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "minipass-flush": "^1.0.5", + "minipass-fetch": "^2.0.3", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "http-cache-semantics": "^4.1.0", + "ssri": "^9.0.0", + "socks-proxy-agent": "^7.0.0", + "cacache": "^16.1.0", + "https-proxy-agent": "^5.0.0", + "promise-retry": "^2.0.1", + "minipass-collect": "^1.0.2", + "http-proxy-agent": "^5.0.0", + "agentkeepalive": "^4.2.1", + "negotiator": "^0.6.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "dev": true, + "peer": true + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "devOptional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/i18n/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vasync": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha512-3oQMomVgQgHzNe5iKuT8PGOhMCQcg1wfh00Nh/Kl39ERdTlw/uNS7kbrhEraDMDKWHdDdc0iBFahPEd/Ft2b+A==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "verror": "1.6.0" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "devOptional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/nodemailer-shared": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", + "integrity": "sha512-68xW5LSyPWv8R0GLm6veAvm7E+XFXkVgvE3FW0FGxNMMZqMkPFeGDVALfR1DPdSfcoO36PnW7q5AAOgFImEZGg==", + "dependencies": { + "nodemailer-fetch": "1.6.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "dev": true + }, + "node_modules/node-gyp/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ldap-filter/node_modules/assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "devOptional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/passport-google-oauth20": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-1.0.0.tgz", + "integrity": "sha512-asv9bVBYf0F6OBP/17dBbHq74KpKVrIQLKPpPmUoR2Wwxv9ZlmNv6QpIX2m0VPkAilLtQeWewWESsqZeh7ZSWQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "peer": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nconf/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/@types/p-queue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz", + "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/nconf": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.10.0.tgz", + "integrity": "sha512-fKiXMQrpP7CYWJQzKkPPx9hPgmq+YLDyxcG9N8RpiE9FoCkCbzD0NyW0YhE3xn3Aupe7nnDeIx4PFzYehpHT9Q==", + "dependencies": { + "async": "^1.4.0", + "ini": "^1.3.0", + "secure-keys": "^1.0.0", + "yargs": "^3.19.0" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "peer": true + }, + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "devOptional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==", + "optional": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/nconf/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "devOptional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/httpreq": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", + "engines": { + "node": ">= 6.15.1" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "devOptional": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/csv": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/csv/-/csv-5.1.3.tgz", + "integrity": "sha512-uHPF5nxxFgcBQ/Mkicjh+IcQJeooIcN8gS/5mnvIdIccLh3Qf792jXE00ovdYDmABhE0yTMNCZgx3ZsBrR2GoQ==", + "dependencies": { + "csv-generate": "^3.2.3", + "csv-parse": "^4.4.6", + "csv-stringify": "^5.3.3", + "stream-transform": "^2.0.1" + }, + "engines": { + "node": ">= 0.1.90" + } + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "peer": true + }, + "node_modules/mocha/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "optional": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "optional": true, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", + "optional": true + }, + "node_modules/optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", + "dependencies": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha512-lRy4DxuIFWXlJU7ed8UiTJOSTqStqYdEb4CEbtXfNbkdj3nH1L+reUWiE10VWcJS2yR7tge8Z74pJjtBjNwj0w==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "optional": true + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "peer": true + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/sass-graph/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/nconf/node_modules/yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg==", + "dependencies": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "devOptional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redis/node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/node": { + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/path-scurry": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.0.tgz", + "integrity": "sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "devOptional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ical-generator": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-1.15.4.tgz", + "integrity": "sha512-drXe4RLkfNlvDvdy/E6BUI9p+01L3ySK1ufNEYI9TxNKG9ZA3G60QWoZvD1dtmH4scwDxYu6/sZBPJvYVNrj8A==", + "dependencies": { + "moment-timezone": "^0.5.32" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "@types/node": ">= 8.0.0" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nconf/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "minipass-flush": "^1.0.5", + "unique-filename": "^2.0.0", + "infer-owner": "^1.0.4", + "@npmcli/move-file": "^2.0.0", + "promise-inflight": "^1.0.1", + "tar": "^6.1.11", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "lru-cache": "^7.7.1", + "ssri": "^9.0.0", + "chownr": "^2.0.0", + "mkdirp": "^1.0.4", + "minipass-collect": "^1.0.2", + "fs-minipass": "^2.1.0", + "@npmcli/fs": "^2.1.0", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "glob": "^8.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/true-case-path": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", + "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==", + "dev": true + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "dependencies": { + "precond": "0.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "node_modules/mysql2": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.7.tgz", + "integrity": "sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==", + "optional": true, + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@messageformat/number-skeleton": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz", + "integrity": "sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==", + "dev": true + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@types/p-cancelable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/p-cancelable/-/p-cancelable-1.0.1.tgz", + "integrity": "sha512-MGdhuVx7X2yJe4dgOnDQcZQAYgiC/QK1O5HUPgTMTxWYiOlyWEO5DWmPBlXQBU1F6/JM7aSgYBDrpt7kurC6dw==", + "deprecated": "This is a stub types definition. p-cancelable provides its own type definitions, so you do not need this installed.", + "dependencies": { + "p-cancelable": "*" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "devOptional": true + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "devOptional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dev": true, + "dependencies": { + "boolean": "^3.1.4" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/cliui/node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "devOptional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cls-bluebird": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", + "integrity": "sha512-XVb0RPmHQyy35Tz9z34gvtUcBKUK8A/1xkGCyeFc9B0C7Zr5SysgFaswRVdwI5NEMcO+3JKlIDGIOgERSn9NdA==", + "dependencies": { + "is-bluebird": "^1.0.2", + "shimmer": "^1.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/@types/form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-23/wYiuckYYtFpL+4RPWiWmRQH2BjFuqCUi2+N3amB1a1Drv+i/byTrGvlLwRVLFNAZbwpbQ7JvTK+VCAPMbcg==", + "deprecated": "This is a stub types definition. form-data provides its own type definitions, so you do not need this installed.", + "dependencies": { + "form-data": "*" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", + "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", + "optional": true, + "engines": { + "node": "^16 || ^18 || >= 20" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@types/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-LkZCWg4JxFdQR/nGNZcMiyKAbNG3DKBRS6nn6Hg4dLS82zxkdBJJcvf4zXFvDCEI+e4dZdQX6wreqs9RDGMRfw==", + "deprecated": "This is a stub types definition. p-retry provides its own type definitions, so you do not need this installed.", + "dependencies": { + "p-retry": "*" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": { + "gopd": "^1.0.1", + "data-view-buffer": "^1.0.1", + "regexp.prototype.flags": "^1.5.2", + "object-keys": "^1.1.1", + "has-proto": "^1.0.3", + "is-array-buffer": "^3.0.4", + "es-set-tostringtag": "^2.0.3", + "internal-slot": "^1.0.7", + "data-view-byte-length": "^1.0.1", + "typed-array-buffer": "^1.0.2", + "es-object-atoms": "^1.0.0", + "es-to-primitive": "^1.2.1", + "hasown": "^2.0.2", + "get-intrinsic": "^1.2.4", + "is-weakref": "^1.0.2", + "is-callable": "^1.2.7", + "string.prototype.trimend": "^1.0.8", + "is-data-view": "^1.0.1", + "available-typed-arrays": "^1.0.7", + "is-shared-array-buffer": "^1.0.3", + "object.assign": "^4.1.5", + "arraybuffer.prototype.slice": "^1.0.3", + "data-view-byte-offset": "^1.0.0", + "is-regex": "^1.1.4", + "is-typed-array": "^1.1.13", + "es-define-property": "^1.0.0", + "has-symbols": "^1.0.3", + "array-buffer-byte-length": "^1.0.1", + "string.prototype.trim": "^1.2.9", + "get-symbol-description": "^1.0.2", + "safe-regex-test": "^1.0.3", + "unbox-primitive": "^1.0.2", + "safe-array-concat": "^1.1.2", + "is-string": "^1.0.7", + "call-bind": "^1.0.7", + "function.prototype.name": "^1.1.6", + "typed-array-byte-length": "^1.0.1", + "object-inspect": "^1.13.1", + "globalthis": "^1.0.3", + "typed-array-length": "^1.0.6", + "which-typed-array": "^1.1.15", + "has-property-descriptors": "^1.0.2", + "typed-array-byte-offset": "^1.0.2", + "string.prototype.trimstart": "^1.0.8", + "is-negative-zero": "^2.0.3", + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/busboy/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/follow-redirects/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "peer": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/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==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "optional": true, + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "devOptional": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.3.14", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.14.tgz", + "integrity": "sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw==", + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/vasync/node_modules/extsprintf": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha512-T3PYC6HucmF4OfunfZb5d1nRvTSvWYhsr/Og33HANcCuCtGPUtWVyt/tTs8SU9sR0SGh5Z/xQCuX/D72ph2H+A==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-plural": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.4.0.tgz", + "integrity": "sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==", + "dev": true + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "peer": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globule": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", + "dev": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "^4.17.21", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/passport": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "devOptional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "engines": [ + "node >=0.10.0" + ], + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "node_modules/optimist/node_modules/minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==" + }, + "node_modules/csv-parse": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz", + "integrity": "sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "dependencies": { + "punycode": "2.x.x" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "peer": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stream-transform": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz", + "integrity": "sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==", + "dependencies": { + "mixme": "^0.5.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/@handlebars/allow-prototype-access": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@handlebars/allow-prototype-access/-/allow-prototype-access-1.0.5.tgz", + "integrity": "sha512-D5AJO9dM6B+1scVVQ4ma05cAku5M2dEnlycggIg6RW539TdQlgbNg1G1r86fNqqNI0CHv/TVwKEB9M6ILJQCWQ==", + "peerDependencies": { + "handlebars": "^4.7.2" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/busboy/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/node-gyp/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "devOptional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "devOptional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-interval-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", + "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "peer": true + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/connect-redis": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz", + "integrity": "sha512-aaNluLlAn/3JPxRwdzw7lhvEoU6Enb+d83xnokUNhC9dktqBoawKWL+WuxinxvBLTz6q9vReTnUDnUslaz74aw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "devOptional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/mocha": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", + "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", + "dev": true, + "dependencies": { + "yargs-unparser": "1.6.0", + "js-yaml": "3.13.1", + "yargs": "13.3.2", + "which": "1.3.1", + "ansi-colors": "3.2.3", + "log-symbols": "2.2.0", + "wide-align": "1.1.3", + "growl": "1.10.5", + "diff": "3.5.0", + "object.assign": "4.1.0", + "node-environment-flags": "1.0.5", + "yargs-parser": "13.1.2", + "ms": "2.1.1", + "supports-color": "6.0.0", + "strip-json-comments": "2.0.1", + "debug": "3.2.6", + "minimatch": "3.0.4", + "mkdirp": "0.5.4", + "find-up": "3.0.0", + "browser-stdout": "1.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.3", + "he": "1.2.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@messageformat/runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", + "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", + "dev": true, + "dependencies": { + "make-plural": "^7.0.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sequelize-cli": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-5.5.1.tgz", + "integrity": "sha512-ZM4kUZvY3y14y+Rq3cYxGH7YDJz11jWHcN2p2x7rhAIemouu4CEXr5ebw30lzTBtyXV4j2kTO+nUjZOqzG7k+Q==", + "dependencies": { + "bluebird": "^3.5.3", + "cli-color": "^1.4.0", + "fs-extra": "^7.0.1", + "js-beautify": "^1.8.8", + "lodash": "^4.17.5", + "resolve": "^1.5.0", + "umzug": "^2.1.0", + "yargs": "^13.1.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/redis": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "optional": true, + "dependencies": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-redis" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globule/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/i18n/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/eslint-plugin-mocha": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.4.3.tgz", + "integrity": "sha512-emc4TVjq5Ht0/upR+psftuz6IBG5q279p+1dSRDeHf+NS9aaerBi3lXKo1SEzwC29hFIW21gO89CEWSvRsi8IQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "globals": "^13.24.0", + "rambda": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/@types/ws": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", + "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", + "dependencies": { + "@types/events": "*", + "@types/node": "*" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "devOptional": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/topo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", + "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", + "deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", + "dependencies": { + "hoek": "6.x.x" + } + }, + "node_modules/p-queue": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz", + "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==", + "engines": { + "node": ">=4" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "devOptional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hoek": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==", + "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues." + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/request-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", + "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "devOptional": true + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/busboy/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "peer": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/node-abi": { + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "peer": true + }, + "node_modules/passport-http-bearer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz", + "integrity": "sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vasync/node_modules/verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha512-bIOaZx4+Bf6a7sIORfmYnyKLDLk/lhVym6rjYlq+vkitYKnhFmUpmPpDTCltWFrUTlGKs6sCeoDWfMA0oOOneA==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "extsprintf": "1.2.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ldapauth-fork": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-4.3.3.tgz", + "integrity": "sha512-x76VpQ5ZqkwAJmqwcD6KIwDiNEbgIGIPGwC/eA17e1dxWhlTx36w0DlLOFwjTuZ2iuaLTsZsUprlVqvSlwc/1Q==", + "dependencies": { + "@types/ldapjs": "^1.0.0", + "@types/node": "*", + "bcryptjs": "^2.4.0", + "ldapjs": "^1.0.2", + "lru-cache": "^5.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/httpntlm/node_modules/underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" + }, + "node_modules/mixme": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.5.10.tgz", + "integrity": "sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==", + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/jsprim/node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/node-gyp/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "devOptional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "optional": true, + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "devOptional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", + "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/node-gyp/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "devOptional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/i18n": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz", + "integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==", + "dev": true, + "dependencies": { + "@messageformat/core": "^3.0.0", + "debug": "^4.3.3", + "fast-printf": "^1.6.9", + "make-plural": "^7.0.0", + "math-interval-parser": "^2.0.1", + "mustache": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/mashpie" + } + }, + "node_modules/dicer/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/nodemailer": { + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/scss-tokenizer": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", + "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", + "dev": true, + "dependencies": { + "js-base64": "^2.4.9", + "source-map": "^0.7.3" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "type-is": "~1.6.18", + "safe-buffer": "5.2.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "body-parser": "1.20.2", + "content-type": "~1.0.4", + "send": "0.18.0", + "cookie": "0.6.0", + "methods": "~1.1.2", + "proxy-addr": "~2.0.7", + "accepts": "~1.3.8", + "range-parser": "~1.2.1", + "on-finished": "2.4.1", + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "etag": "~1.8.1", + "path-to-regexp": "0.1.7", + "statuses": "2.0.1", + "parseurl": "~1.3.3", + "setprototypeof": "1.2.0", + "merge-descriptors": "1.0.1", + "vary": "~1.1.2", + "serve-static": "1.15.0", + "content-disposition": "0.5.4", + "escape-html": "~1.0.3", + "http-errors": "2.0.0", + "cookie-signature": "1.0.6", + "utils-merge": "1.0.1", + "array-flatten": "1.1.1", + "depd": "2.0.0", + "qs": "6.11.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/node-gyp/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "devOptional": true, + "dependencies": { + "minipass-flush": "^1.0.5", + "unique-filename": "^1.1.1", + "infer-owner": "^1.0.4", + "@npmcli/move-file": "^1.0.1", + "promise-inflight": "^1.0.1", + "tar": "^6.0.2", + "minipass": "^3.1.1", + "minipass-pipeline": "^1.2.2", + "lru-cache": "^6.0.0", + "ssri": "^8.0.1", + "chownr": "^2.0.0", + "mkdirp": "^1.0.3", + "minipass-collect": "^1.0.2", + "fs-minipass": "^2.0.0", + "@npmcli/fs": "^1.0.0", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "glob": "^7.1.4" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "devOptional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "devOptional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/p-cancelable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.0.0.tgz", + "integrity": "sha512-USgPoaC6tkTGlS831CxsVdmZmyb8tR1D+hStI84MyckLOzfJlYQUweomrwE3D8T7u5u5GVuW064LT501wHTYYA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/stdout-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dicer/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "devOptional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/scss-tokenizer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/log-symbols/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", + "dependencies": { + "cookie": "0.6.0", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "devOptional": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sass-graph": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", + "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "lodash": "^4.17.11", + "scss-tokenizer": "^0.4.3", + "yargs": "^17.2.1" + }, + "bin": { + "sassgraph": "bin/sassgraph" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "devOptional": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "devOptional": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/multer": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", + "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", + "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", + "optional": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "devOptional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "devOptional": true + }, + "node_modules/busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", + "dependencies": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/rambda": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", + "dev": true + }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "devOptional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/connect-session-sequelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-6.0.0.tgz", + "integrity": "sha512-XC71xJd5rqObdL7700S/qFD+gSRA4o6WVJAyFY0Vjah73id5bBElM0SHQR1ME5Bxrt4JL8alvggseNDVTlKyxA==", + "dependencies": { + "debug": "^3.1.0", + "deep-equal": "^1.0.1" + }, + "engines": { + "node": ">= 5" + }, + "peerDependencies": { + "sequelize": ">= 3.24.5" + } + }, + "node_modules/yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "devOptional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/ldapjs": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.11.tgz", + "integrity": "sha512-O4D1frY6xy2mQr5WouNPeltMe5EHdmU4FxbLDC6TMDX5HXOuafusGu+7Y9WAoqBaYHZ5hcFa7jfkpggyexfeXQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "devOptional": true + }, + "node_modules/secure-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", + "integrity": "sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==" + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", + "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "devOptional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/stdout-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/pg-protocol": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", + "optional": true + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/retry-as-promised": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", + "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", + "dependencies": { + "any-promise": "^1.3.0" + } + }, + "node_modules/@messageformat/date-skeleton": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz", + "integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha512-6i37w/+EhlWlGUJff3T/Q8u1RGmP5wgbiwYnOnbOqvtrPxT63/sYFyP9RcpxtxGymtfA075IvmOnL7ycNOWl3w==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "devOptional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "devOptional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "devOptional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/serve-favicon/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/gauge/node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "devOptional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/sequelize": { + "version": "5.22.5", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.5.tgz", + "integrity": "sha512-ySIHof18sJbeVG4zjEvsDL490cd9S14/IhkCrZR/g0C/FPlZq1AzEJVeSAo++9/sgJH2eERltAIGqYQNgVqX/A==", + "deprecated": "Please update to v6 or higher! A migration guide can be found here: https://sequelize.org/v6/manual/upgrade-to-v6.html", + "dependencies": { + "bluebird": "^3.5.0", + "cls-bluebird": "^2.1.0", + "debug": "^4.1.1", + "dottie": "^2.0.0", + "inflection": "1.12.0", + "lodash": "^4.17.15", + "moment": "^2.24.0", + "moment-timezone": "^0.5.21", + "retry-as-promised": "^3.2.0", + "semver": "^6.3.0", + "sequelize-pool": "^2.3.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.4.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/connect-session-sequelize/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/read-pkg/node_modules/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, + "engines": { + "node": ">=8" + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index dc1839a14..d49751e49 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,85 @@ { "name": "TimeOff.Management", - "version": "0.6.2", + "version": "1.0.0", "private": false, "description": "Simple yet powerful absence management software for small and medium size business", + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "mysql2": "^3.9.1", + "pg": "^8.11.4", + "redis": "^3.1.2", + "sqlite3": "^5.1.6" + }, "dependencies": { - "bluebird": "^2.10.2", - "body-parser": "^1.8.4", - "connect-session-sequelize": "3.0.0", - "cookie-parser": "^1.3.5", - "debug": "~2.0.0", - "express": "^4.13.4", - "express-handlebars": "^3.0.0", - "express-session": "^1.13.0", - "formidable": "~1.0.17", - "html-to-text": "^3.2.0", - "ical-generator": "^0.2.7", - "ldapauth-fork": "^2.5.2", - "moment": "^2.11.2", - "moment-timezone": "^0.5.10", - "morgan": "^1.3.2", - "nconf": "^0.8.4", - "node-uuid": "^1.4.7", - "nodemailer": "^1.11.0", - "nodemailer-smtp-transport": "^1.1.0", + "@handlebars/allow-prototype-access": "^1.0.5", + "@slack/client": "^4.1.0", + "bluebird": "^3.5.4", + "body-parser": "^1.19.0", + "connect-redis": "^6.0.0", + "connect-session-sequelize": "6.0.0", + "cookie-parser": "^1.4.4", + "csv": "~5.1.1", + "debug": "~4.1.1", + "dotenv": "^16.4.5", + "express": "^4.16.4", + "express-handlebars": "^3.0.2", + "express-session": "^1.16.1", + "formidable": "~1.2.1", + "html-to-text": "^5.1.1", + "ical-generator": "^1.7.1", + "joi": "~14.3.1", + "ldapauth-fork": "^4.2.0", + "moment": "^2.24.0", + "moment-timezone": "^0.5.25", + "morgan": "^1.9.1", + "multer": "^1.4.1", + "nconf": "^0.10.0", + "node-uuid": "^1.4.8", + "nodemailer": "^6.1.1", + "nodemailer-smtp-transport": "^2.7.4", "optimist": "^0.6.1", - "passport": "^0.3.2", + "passport": "^0.4.0", + "passport-google-oauth20": "^1.0.0", + "passport-http-bearer": "^1.0.1", "passport-local": "^1.0.0", - "sequelize": "^3.19.2", - "sequelize-cli": "2.5.1", - "serve-favicon": "^2.1.7", - "sqlite3": "^3.1.1", - "underscore": "^1.8.3", - "validator": "^3.43.0" + "sequelize": "^5.22.5", + "sequelize-cli": "^5.5.1", + "serve-favicon": "^2.5.0", + "underscore": "^1.9.1", + "uuid": "^3.3.2", + "validator": "^13.11.0" }, "devDependencies": { - "chai": "^2.2.0", - "mocha": "^2.2.4", - "selenium-webdriver": "2.45.1" + "chai": "^4.2.0", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-mocha": "^10.2.0", + "eslint-plugin-n": "^16.2.0", + "eslint-plugin-promise": "^6.1.1", + "i18n": "^0.15.1", + "mocha": "^6.2.2", + "node-sass": "^8.0.0", + "prettier": "1.17.0", + "request-promise": "^4.2.4", + "selenium-webdriver": "^3.6.0" }, "scripts": { - "test": "node node_modules/mocha/bin/mocha --recursive t/*", + "prettier": "prettier --write \"**/*.js\"", + "lint": "eslint --ext .js \"**/*.js\"", + "lint:fix": "eslint --ext .js \"**/*.js\" --fix", + "test": "node node_modules/mocha/bin/mocha --recursive t", + "test:chrome": "USE_CHROME=1 node node_modules/mocha/bin/mocha --recursive t", "start": "node bin/wwww", - "db-update": "node node_modules/.bin/sequelize db:migrate --config=config/db.json --models-path=lib/model/db/" + "db-update": "node node_modules/.bin/sequelize db:migrate --config=config/db.json --models-path=lib/model/db/", + "carry-over-allowance": "node bin/calculate_carry_over_allowance_for_all_users.js", + "compile-sass": "node node_modules/node-sass/bin/node-sass scss/main.scss public/css/style.css", + "preinstall": "npx npm-force-resolutions" + }, + "resolutions": { + "graceful-fs": "4.2.11" } } diff --git a/public/css/bootstrap-datepicker3.standalone.css b/public/css/bootstrap-datepicker3.standalone.css index 409993cc8..415873174 100644 --- a/public/css/bootstrap-datepicker3.standalone.css +++ b/public/css/bootstrap-datepicker3.standalone.css @@ -24,7 +24,7 @@ padding: 4px; } .datepicker-dropdown:before { - content: ''; + content: ""; display: inline-block; border-left: 7px solid transparent; border-right: 7px solid transparent; @@ -34,7 +34,7 @@ position: absolute; } .datepicker-dropdown:after { - content: ''; + content: ""; display: inline-block; border-left: 6px solid transparent; border-right: 6px solid transparent; @@ -652,7 +652,12 @@ fieldset[disabled] .datepicker table tr td.active.highlighted.focus { .open > .dropdown-toggle.datepicker table tr td span.active:hover, .open > .dropdown-toggle.datepicker table tr td span.active:hover:hover, .open > .dropdown-toggle.datepicker table tr td span.active.disabled:hover, -.open > .dropdown-toggle.datepicker table tr td span.active.disabled:hover:hover, +.open +> .dropdown-toggle.datepicker +table +tr +td +span.active.disabled:hover:hover, .datepicker table tr td span.active:active:focus, .datepicker table tr td span.active:hover:active:focus, .datepicker table tr td span.active.disabled:active:focus, @@ -664,7 +669,12 @@ fieldset[disabled] .datepicker table tr td.active.highlighted.focus { .open > .dropdown-toggle.datepicker table tr td span.active:focus, .open > .dropdown-toggle.datepicker table tr td span.active:hover:focus, .open > .dropdown-toggle.datepicker table tr td span.active.disabled:focus, -.open > .dropdown-toggle.datepicker table tr td span.active.disabled:hover:focus, +.open +> .dropdown-toggle.datepicker +table +tr +td +span.active.disabled:hover:focus, .datepicker table tr td span.active:active.focus, .datepicker table tr td span.active:hover:active.focus, .datepicker table tr td span.active.disabled:active.focus, @@ -676,7 +686,12 @@ fieldset[disabled] .datepicker table tr td.active.highlighted.focus { .open > .dropdown-toggle.datepicker table tr td span.active.focus, .open > .dropdown-toggle.datepicker table tr td span.active:hover.focus, .open > .dropdown-toggle.datepicker table tr td span.active.disabled.focus, -.open > .dropdown-toggle.datepicker table tr td span.active.disabled:hover.focus { +.open +> .dropdown-toggle.datepicker +table +tr +td +span.active.disabled:hover.focus { color: #ffffff; background-color: #285e8e; border-color: #193c5a; diff --git a/public/css/bootstrap.min.css b/public/css/bootstrap.min.css index 03759e110..aafdbe379 100755 --- a/public/css/bootstrap.min.css +++ b/public/css/bootstrap.min.css @@ -7,8 +7,7133 @@ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=2f747106c336fe71d867) * Config saved to config.json and https://gist.github.com/2f747106c336fe71d867 - *//*! + */ /*! * Bootstrap v3.3.6 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover,a.text-primary:focus{color:#286090}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover,a.bg-primary:focus{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f7f8;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f7f8}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f7f8}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e6ebed}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;-o-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);-webkit-background-clip:padding-box;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f7f8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#337ab7}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f5f7f8;border-color:#e2e7ea}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e2e7ea}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e2e7ea}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e2e7ea;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e2e7ea}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f7f8;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#337ab7;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#fff;background-color:#337ab7;border-color:#337ab7;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f7f8;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f7f8}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f7f8;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f7f8;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f7f8;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f7f8;border:1px solid #e0e6e9;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-webkit-gradient(linear, left top, right top, color-stop(0, rgba(0,0,0,0.5)), to(rgba(0,0,0,0.0001)));background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-webkit-gradient(linear, left top, right top, color-stop(0, rgba(0,0,0,0.0001)), to(rgba(0,0,0,0.5)));background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}} \ No newline at end of file + */ /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +mark { + background: #ff0; + color: #000; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: "Glyphicons Halflings"; + src: url("../fonts/glyphicons-halflings-regular.eot"); + src: url("../fonts/glyphicons-halflings-regular.eot?#iefix") + format("embedded-opentype"), + url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"), + url("../fonts/glyphicons-halflings-regular.woff") format("woff"), + url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"), + url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") + format("svg"); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: "Glyphicons Halflings"; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + background-color: #fcf8e3; + padding: 0.2em; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; + margin-left: -5px; +} +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: "\2014 \00A0"; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eee; + border-left: 0; + text-align: right; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ""; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: "\00A0 \2014"; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + word-break: break-all; + word-wrap: break-word; + color: #333; + background-color: #f5f7f8; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +.row { + margin-left: -15px; + margin-right: -15px; +} +.col-xs-1, +.col-sm-1, +.col-md-1, +.col-lg-1, +.col-xs-2, +.col-sm-2, +.col-md-2, +.col-lg-2, +.col-xs-3, +.col-sm-3, +.col-md-3, +.col-lg-3, +.col-xs-4, +.col-sm-4, +.col-md-4, +.col-lg-4, +.col-xs-5, +.col-sm-5, +.col-md-5, +.col-lg-5, +.col-xs-6, +.col-sm-6, +.col-md-6, +.col-lg-6, +.col-xs-7, +.col-sm-7, +.col-md-7, +.col-lg-7, +.col-xs-8, +.col-sm-8, +.col-md-8, +.col-lg-8, +.col-xs-9, +.col-sm-9, +.col-md-9, +.col-lg-9, +.col-xs-10, +.col-sm-10, +.col-md-10, +.col-lg-10, +.col-xs-11, +.col-sm-11, +.col-md-11, +.col-lg-11, +.col-xs-12, +.col-sm-12, +.col-md-12, +.col-lg-12 { + position: relative; + min-height: 1px; + padding-left: 15px; + padding-right: 15px; +} +.col-xs-1, +.col-xs-2, +.col-xs-3, +.col-xs-4, +.col-xs-5, +.col-xs-6, +.col-xs-7, +.col-xs-8, +.col-xs-9, +.col-xs-10, +.col-xs-11, +.col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, + .col-sm-2, + .col-sm-3, + .col-sm-4, + .col-sm-5, + .col-sm-6, + .col-sm-7, + .col-sm-8, + .col-sm-9, + .col-sm-10, + .col-sm-11, + .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, + .col-md-2, + .col-md-3, + .col-md-4, + .col-md-5, + .col-md-6, + .col-md-7, + .col-md-8, + .col-md-9, + .col-md-10, + .col-md-11, + .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, + .col-lg-2, + .col-lg-3, + .col-lg-4, + .col-lg-5, + .col-lg-6, + .col-lg-7, + .col-lg-8, + .col-lg-9, + .col-lg-10, + .col-lg-11, + .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f7f8; +} +table col[class*="col-"] { + position: static; + float: none; + display: table-column; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + float: none; + display: table-cell; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f7f8; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e6ebed; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + overflow-x: auto; + min-height: 0.01%; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + padding: 0; + margin: 0; + border: 0; + min-width: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out 0.15s, + -webkit-box-shadow ease-in-out 0.15s; + -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), + 0 0 8px rgba(102, 175, 233, 0.6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + border: 0; + background-color: transparent; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; + min-height: 34px; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-left: 0; + padding-right: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + border-color: #3c763d; + background-color: #dff0d8; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + border-color: #8a6d3b; + background-color: #fcf8e3; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + border-color: #a94442; + background-color: #f2dede; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: 7px; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-left: -15px; + margin-right: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + margin-bottom: 0; + padding-top: 7px; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + white-space: nowrap; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + border-radius: 4px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + outline: 0; + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + color: #337ab7; + font-weight: normal; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; + -webkit-transition-duration: 0.35s; + -o-transition-duration: 0.35s; + transition-duration: 0.35s; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + text-align: left; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + text-decoration: none; + color: #262626; + background-color: #f5f7f8; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + outline: 0; + background-color: #337ab7; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: not-allowed; +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + left: auto; + right: 0; +} +.dropdown-menu-left { + left: 0; + right: auto; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + left: auto; + right: 0; + } + .navbar-right .dropdown-menu-left { + left: 0; + right: auto; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical + > .btn-group:first-child:not(:last-child) + > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical + > .btn-group:last-child:not(:first-child) + > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + float: none; + display: table-cell; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; + height: 50px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 8px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), + 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), + 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 8px; + margin-bottom: 8px; +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f5f7f8; + border-color: #e2e7ea; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e2e7ea; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e2e7ea; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: #e2e7ea; + color: #555; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e2e7ea; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + background-color: #080808; + color: #fff; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f7f8; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + content: "/\00a0"; + padding: 0 5px; + color: #ccc; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + line-height: 1.42857143; + text-decoration: none; + color: #337ab7; + background-color: #fff; + border: 1px solid #ddd; + margin-left: -1px; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; + cursor: default; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + background-color: #fff; + border-color: #ddd; + cursor: not-allowed; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 6px; + border-top-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-bottom-right-radius: 6px; + border-top-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + list-style: none; + text-align: center; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + background-color: #fff; + cursor: not-allowed; +} +.label { + display: inline; + padding: 0.2em 0.6em 0.3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + color: #fff; + line-height: 1; + vertical-align: middle; + white-space: nowrap; + text-align: center; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; + padding-left: 15px; + padding-right: 15px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border 0.2s ease-in-out; + -o-transition: border 0.2s ease-in-out; + transition: border 0.2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-left: auto; + margin-right: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #3c763d; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #31708f; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + background-color: #fcf8e3; + border-color: #faebcc; + color: #8a6d3b; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + background-color: #f2dede; + border-color: #ebccd1; + color: #a94442; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f7f8; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); + background-image: linear-gradient( + 45deg, + rgba(255, 255, 255, 0.15) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, 0.15) 50%, + rgba(255, 255, 255, 0.15) 75%, + transparent 75%, + transparent + ); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + zoom: 1; + overflow: hidden; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + text-decoration: none; + color: #555; + background-color: #f5f7f8; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + background-color: #eee; + color: #777; + cursor: not-allowed; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f7f8; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel + > .panel-collapse + > .list-group:first-child + .list-group-item:first-child { + border-top: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel + > .panel-heading + + .panel-collapse + > .list-group + .list-group-item:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-left: 15px; + padding-right: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > thead:first-child + > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > tbody:first-child + > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > thead:first-child + > tr:first-child + td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > tbody:first-child + > tr:first-child + td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > thead:first-child + > tr:first-child + th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel + > .table-responsive:first-child + > .table:first-child + > tbody:first-child + > tr:first-child + th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel + > .table-responsive:first-child + > .table:first-child + > thead:first-child + > tr:first-child + td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel + > .table-responsive:first-child + > .table:first-child + > tbody:first-child + > tr:first-child + td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel + > .table-responsive:first-child + > .table:first-child + > thead:first-child + > tr:first-child + th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel + > .table-responsive:first-child + > .table:first-child + > tbody:first-child + > tr:first-child + th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tbody:last-child + > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tfoot:last-child + > tr:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tbody:last-child + > tr:last-child + td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tfoot:last-child + > tr:last-child + td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tbody:last-child + > tr:last-child + th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tfoot:last-child + > tr:last-child + th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tbody:last-child + > tr:last-child + td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tfoot:last-child + > tr:last-child + td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tbody:last-child + > tr:last-child + th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel + > .table-responsive:last-child + > .table:last-child + > tfoot:last-child + > tr:last-child + th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f7f8; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f7f8; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f7f8; + border: 1px solid #e0e6e9; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.modal-open { + overflow: hidden; +} +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + -webkit-background-clip: padding-box; + background-clip: padding-box; + outline: 0; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + right: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-style: normal; + font-weight: normal; + letter-spacing: normal; + line-break: auto; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 14px; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top > .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top > .arrow:after { + content: " "; + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #fff; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right > .arrow:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #fff; +} +.popover.bottom > .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom > .arrow:after { + content: " "; + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left > .arrow:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: #fff; + bottom: -10px; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform 0.6s ease-in-out; + -o-transition: -o-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + left: 0; + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + left: 0; + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + left: 0; + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 15%; + opacity: 0.5; + filter: alpha(opacity=50); + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background-color: rgba(0, 0, 0, 0); +} +.carousel-control.left { + background-image: -webkit-linear-gradient( + left, + rgba(0, 0, 0, 0.5) 0, + rgba(0, 0, 0, 0.0001) 100% + ); + background-image: -o-linear-gradient( + left, + rgba(0, 0, 0, 0.5) 0, + rgba(0, 0, 0, 0.0001) 100% + ); + background-image: -webkit-gradient( + linear, + left top, + right top, + color-stop(0, rgba(0, 0, 0, 0.5)), + to(rgba(0, 0, 0, 0.0001)) + ); + background-image: linear-gradient( + to right, + rgba(0, 0, 0, 0.5) 0, + rgba(0, 0, 0, 0.0001) 100% + ); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} +.carousel-control.right { + left: auto; + right: 0; + background-image: -webkit-linear-gradient( + left, + rgba(0, 0, 0, 0.0001) 0, + rgba(0, 0, 0, 0.5) 100% + ); + background-image: -o-linear-gradient( + left, + rgba(0, 0, 0, 0.0001) 0, + rgba(0, 0, 0, 0.5) 100% + ); + background-image: -webkit-gradient( + linear, + left top, + right top, + color-stop(0, rgba(0, 0, 0, 0.0001)), + to(rgba(0, 0, 0, 0.5)) + ); + background-image: linear-gradient( + to right, + rgba(0, 0, 0, 0.0001) 0, + rgba(0, 0, 0, 0.5) 100% + ); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} +.carousel-control:hover, +.carousel-control:focus { + outline: 0; + color: #fff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + margin-top: -10px; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + line-height: 1; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: "\2039"; +} +.carousel-control .icon-next:before { + content: "\203a"; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid #fff; + border-radius: 10px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); +} +.carousel-indicators .active { + margin: 0; + width: 12px; + height: 12px; + background-color: #fff; +} +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + content: " "; + display: table; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} diff --git a/public/css/font-awesome.min.css b/public/css/font-awesome.min.css index ee4e9782b..67d975b06 100644 --- a/public/css/font-awesome.min.css +++ b/public/css/font-awesome.min.css @@ -1,4 +1,2026 @@ /*! * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"} + */ +@font-face { + font-family: "FontAwesome"; + src: url("../fonts/fontawesome-webfont.eot?v=4.4.0"); + src: url("../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0") + format("embedded-opentype"), + url("../fonts/fontawesome-webfont.woff2?v=4.4.0") format("woff2"), + url("../fonts/fontawesome-webfont.woff?v=4.4.0") format("woff"), + url("../fonts/fontawesome-webfont.ttf?v=4.4.0") format("truetype"), + url("../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular") + format("svg"); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: 0.2em 0.25em 0.15em; + border: solid 0.08em #eee; + border-radius: 0.1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: 0.3em; +} +.fa.fa-pull-right { + margin-left: 0.3em; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: 0.3em; +} +.fa.pull-right { + margin-left: 0.3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #fff; +} +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} diff --git a/public/css/style.css b/public/css/style.css index e82a3d6fb..b8b5be34f 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -3,6 +3,27 @@ (from witin project rooot directory) sassc scss/main.scss public/css/style.css */ +/* Example CSS Variables setup */ +:root { + --background-color: #fff; + --text-color: #000; + --hover-text-color: #555; + /* Hover text color for light theme */ + --hover-bg-color: #eee; + /* Hover background color for light theme */ } + +.dark-mode { + --background-color: #212121; + --secondary-background-color: #1e1e1e; + --accent-color: #0fcc76; + --text-color: #ffffff; + --subtle-text-color: #b3b3b3; + --divider-color: #333333; } + +body { + background-color: var(--background-color); + color: var(--text-color); } + body { font: 14px "Open Sans", Helvetica, Arial, sans-serif; color: #61686b; } @@ -10,11 +31,92 @@ body { a { color: #5ba4e5; } +body { + font: 14px "Open Sans", Helvetica, Arial, sans-serif; + color: #61686b; } + +body.dark-mode { + color: var(--text-color); } + +a { + color: #5ba4e5; } + +/* Dark mode specific styles */ +body.dark-mode a { + color: #5ba4e5; } + +body.dark-mode [role='tooltip'] { + background-color: #333; + color: #fff; + border-color: #555; } + +body.dark-mode tr:hover { + color: #0fcc76; } + +body.dark-mode .modal-content, +body.dark-mode .navbar-collapse, +body.dark-mode .panel-default, +body.dark-mode .container-fluid { + background-color: var(--background-color); + color: var(--text-color); } + +body.dark-mode .form-control, +body.dark-mode .list-group, +body.dark-mode .datepicker-days, +body.dark-mode .container-fluid:hover { + color: var(--hover-text-color); + background-color: var(--hover-bg-color); } + +/* end of dark mode specific styles */ +/* + Simple class that makes its content's font size little bit bigger + */ +.text-bigger { + font-size: 16px; } + +/* + Add special class to remove default buttom space +*/ +.no-bottom-space { + margin-bottom: 0px; } + +.large-bottom-space { + margin-bottom: 300px; } + +/* + Increase space between form groups so they are more prominent +*/ +.form-group { + margin-bottom: 20px; } + +/* + Make help blocks in forms to be more distinguishable + */ +.form-group .help-block { + font-style: italic; } + +/* + Make certain progress bars to be taller + */ +.progress.bigger { + height: 38px; } + .progress.bigger .progress-bar { + line-height: 38px; } + /* Space out content a bit */ body { padding-top: 20px; padding-bottom: 20px; } +label.label-plain { + font-weight: normal; } + +/* Make breadcrumbs to be transparant */ +.breadcrumb { + background: none; + padding-right: 0; + padding-left: 0; } + /* Make the masthead heading the same height as the navigation */ .header h3 { padding-bottom: 19px; @@ -28,7 +130,9 @@ body { padding-top: 20px; border-top: 1px solid #e5e5e5; } -.header, .marketing, .footer { +.header, +.marketing, +.footer { padding-right: 0; padding-left: 0; } @@ -36,14 +140,37 @@ body { .header { margin-bottom: 30px; } +.popover { + min-width: 300px; } + /* styling calendar*/ +.calendar_month { + width: 100%; } + +.calendar_month td { + text-align: center; + padding-top: 5px; + padding-bottom: 5px; } + +.calendar_month thead { + text-align: center; + font-weight: bolder; } + div.month_container { height: 250px; } -.calendar_month { +.team-view-table { width: 100%; } -.calendar_month td { +.team-view-table td { + padding-top: 5px; + padding-bottom: 5px; } + +td.team-view-header { + height: 43px; + text-align: center; } + +td.calendar_cell { width: 15px !important; min-width: 15px !important; max-width: 15px !important; @@ -52,42 +179,45 @@ div.month_container { padding-top: 5px; padding-bottom: 5px; } -.calendar_month td span { +td.calendar_cell span { position: relative; - left: 10px; + left: 9px; top: -1px; display: block; text-align: center; z-index: 10; } td.current_day_cell { - background: #e25440; + background: #ffcdd2; color: #ffffff; } td.weekend_cell { - background: #F5F7F8; } + background: #f5f7f8; } td.bank_holiday_cell { - background: #31B0D5; + background: #31b0d5; color: #ffffff; } td.leave_cell { background: #9fbb58; - color: #ffffff; } + color: #61686b; + font-weight: bolder; } td.leave_cell_pended { - background: #FFCBA4; - color: #61686b; } + background: #ffcba4; + color: #61686b; + font-weight: bolder; } .modal-dialog { max-width: 450px; } -.team-view-users td { - padding-top: 5px; - padding-bottom: 5px; } - -td.team-view-header { - height: 43px; } +.left-column-cell { + text-overflow: ellipsis; + /* Cannot avoid specifying width with pixels... */ + width: 165px; + white-space: nowrap; + overflow: hidden; + display: inline-block; } .main-row_header { /* TODO refactore */ @@ -104,7 +234,7 @@ td.team-view-header { /* Feeds */ .feed-calendar-big { font-size: 450px; - color: #F8F8F8; } + color: #f8f8f8; } .feed-calendar-big-picture-holder { text-align: center; } @@ -158,6 +288,22 @@ div.feeds-holder { body.modal-open { position: fixed; } } +/* To be used for selected items in lists */ .selected-item { - background: #F5F7F8; + background: #f5f7f8; font-weight: bolder; } + +.leave_type_color_1 { + background: #96c521 !important; } + +.leave_type_color_2 { + background: #40c4ff !important; } + +.leave_type_color_3 { + background: #ff5722 !important; } + +.leave_type_color_4 { + background: #ff80ab !important; } + +.leave_type_color_5 { + background: #00bcd4 !important; } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 000000000..625ffbb2e Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/js/bank_holidays.js b/public/js/bank_holidays.js new file mode 100644 index 000000000..f25ee304d --- /dev/null +++ b/public/js/bank_holidays.js @@ -0,0 +1,25 @@ +$(document).ready(function() { + $('button.bankholiday-remove-btn').on('click', function(e) { + e.stopPropagation() + + const delete_form = $('#delete_bankholiday_form') + delete_form.attr( + 'action', + delete_form.attr('action') + $(this).attr('value') + '/' + ) + + delete_form.submit() + + return false + }) + + $('#bankholiday-import-btn').on('click', function(e) { + e.stopPropagation() + + const import_form = $('#import_bankholiday_form') + + import_form.submit() + + return false + }) +}) diff --git a/public/js/bootstrap-datepicker.js b/public/js/bootstrap-datepicker.js index fb56652e1..6f6f330e5 100644 --- a/public/js/bootstrap-datepicker.js +++ b/public/js/bootstrap-datepicker.js @@ -4,1915 +4,2055 @@ * Copyright 2012 Stefan Petre * Improvements by Andrew Rowls * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */(function(factory){ - if (typeof define === "function" && define.amd) { - define(["jquery"], factory); - } else if (typeof exports === 'object') { - factory(require('jquery')); + */ ;(function(factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory) + } else if (typeof exports === 'object') { + factory(require('jquery')) + } else { + factory(jQuery) + } +})(function($, undefined) { + function UTCDate() { + return new Date(Date.UTC.apply(Date, arguments)) + } + function UTCToday() { + const today = new Date() + return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()) + } + function isUTCEquals(date1, date2) { + return ( + date1.getUTCFullYear() === date2.getUTCFullYear() && + date1.getUTCMonth() === date2.getUTCMonth() && + date1.getUTCDate() === date2.getUTCDate() + ) + } + function alias(method) { + return function() { + return this[method].apply(this, arguments) + } + } + function isValidDate(d) { + return d && !isNaN(d.getTime()) + } + + var DateArray = (function() { + const extras = { + get: function(i) { + return this.slice(i)[0] + }, + contains: function(d) { + // Array.indexOf is not cross-browser; + // $.inArray doesn't work with Dates + const val = d && d.valueOf() + for (let i = 0, l = this.length; i < l; i++) + if (this[i].valueOf() === val) return i + return -1 + }, + remove: function(i) { + this.splice(i, 1) + }, + replace: function(new_array) { + if (!new_array) return + if (!$.isArray(new_array)) new_array = [new_array] + this.clear() + this.push.apply(this, new_array) + }, + clear: function() { + this.length = 0 + }, + copy: function() { + const a = new DateArray() + a.replace(this) + return a + } + } + + return function() { + const a = [] + a.push.apply(a, arguments) + $.extend(a, extras) + return a + } + })() + + // Picker object + + const Datepicker = function(element, options) { + $(element).data('datepicker', this) + this._process_options(options) + + this.dates = new DateArray() + this.viewDate = this.o.defaultViewDate + this.focusDate = null + + this.element = $(element) + this.isInline = false + this.isInput = this.element.is('input') + this.component = this.element.hasClass('date') + ? this.element.find('.add-on, .input-group-addon, .btn') + : false + this.hasInput = this.component && this.element.find('input').length + if (this.component && this.component.length === 0) this.component = false + + this.picker = $(DPGlobal.template) + this._buildEvents() + this._attachEvents() + + if (this.isInline) { + this.picker.addClass('datepicker-inline').appendTo(this.element) } else { - factory(jQuery); + this.picker.addClass('datepicker-dropdown dropdown-menu') + } + + if (this.o.rtl) { + this.picker.addClass('datepicker-rtl') } -}(function($, undefined){ - - function UTCDate(){ - return new Date(Date.UTC.apply(Date, arguments)); - } - function UTCToday(){ - var today = new Date(); - return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()); - } - function isUTCEquals(date1, date2) { - return ( - date1.getUTCFullYear() === date2.getUTCFullYear() && - date1.getUTCMonth() === date2.getUTCMonth() && - date1.getUTCDate() === date2.getUTCDate() - ); - } - function alias(method){ - return function(){ - return this[method].apply(this, arguments); - }; - } - function isValidDate(d) { - return d && !isNaN(d.getTime()); - } - - var DateArray = (function(){ - var extras = { - get: function(i){ - return this.slice(i)[0]; - }, - contains: function(d){ - // Array.indexOf is not cross-browser; - // $.inArray doesn't work with Dates - var val = d && d.valueOf(); - for (var i=0, l=this.length; i < l; i++) - if (this[i].valueOf() === val) - return i; - return -1; - }, - remove: function(i){ - this.splice(i,1); - }, - replace: function(new_array){ - if (!new_array) - return; - if (!$.isArray(new_array)) - new_array = [new_array]; - this.clear(); - this.push.apply(this, new_array); - }, - clear: function(){ - this.length = 0; - }, - copy: function(){ - var a = new DateArray(); - a.replace(this); - return a; - } - }; - - return function(){ - var a = []; - a.push.apply(a, arguments); - $.extend(a, extras); - return a; - }; - })(); - - - // Picker object - - var Datepicker = function(element, options){ - $(element).data('datepicker', this); - this._process_options(options); - - this.dates = new DateArray(); - this.viewDate = this.o.defaultViewDate; - this.focusDate = null; - - this.element = $(element); - this.isInline = false; - this.isInput = this.element.is('input'); - this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false; - this.hasInput = this.component && this.element.find('input').length; - if (this.component && this.component.length === 0) - this.component = false; - - this.picker = $(DPGlobal.template); - this._buildEvents(); - this._attachEvents(); - - if (this.isInline){ - this.picker.addClass('datepicker-inline').appendTo(this.element); - } - else { - this.picker.addClass('datepicker-dropdown dropdown-menu'); - } - - if (this.o.rtl){ - this.picker.addClass('datepicker-rtl'); - } - - this.viewMode = this.o.startView; - - if (this.o.calendarWeeks) - this.picker.find('thead .datepicker-title, tfoot .today, tfoot .clear') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - - this._allow_update = false; - - this.setStartDate(this._o.startDate); - this.setEndDate(this._o.endDate); - this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); - this.setDaysOfWeekHighlighted(this.o.daysOfWeekHighlighted); - this.setDatesDisabled(this.o.datesDisabled); - - this.fillDow(); - this.fillMonths(); - - this._allow_update = true; - - this.update(); - this.showMode(); - - if (this.isInline){ - this.show(); - } - }; - - Datepicker.prototype = { - constructor: Datepicker, - - _process_options: function(opts){ - // Store raw options for reference - this._o = $.extend({}, this._o, opts); - // Processed options - var o = this.o = $.extend({}, this._o); - - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - var lang = o.language; - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - lang = defaults.language; - } - o.language = lang; - - switch (o.startView){ - case 2: - case 'decade': - o.startView = 2; - break; - case 1: - case 'year': - o.startView = 1; - break; - default: - o.startView = 0; - } - - switch (o.minViewMode){ - case 1: - case 'months': - o.minViewMode = 1; - break; - case 2: - case 'years': - o.minViewMode = 2; - break; - default: - o.minViewMode = 0; - } - - switch (o.maxViewMode) { - case 0: - case 'days': - o.maxViewMode = 0; - break; - case 1: - case 'months': - o.maxViewMode = 1; - break; - default: - o.maxViewMode = 2; - } - - o.startView = Math.min(o.startView, o.maxViewMode); - o.startView = Math.max(o.startView, o.minViewMode); - - // true, false, or Number > 0 - if (o.multidate !== true){ - o.multidate = Number(o.multidate) || false; - if (o.multidate !== false) - o.multidate = Math.max(0, o.multidate); - } - o.multidateSeparator = String(o.multidateSeparator); - - o.weekStart %= 7; - o.weekEnd = (o.weekStart + 6) % 7; - - var format = DPGlobal.parseFormat(o.format); - if (o.startDate !== -Infinity){ - if (!!o.startDate){ - if (o.startDate instanceof Date) - o.startDate = this._local_to_utc(this._zero_time(o.startDate)); - else - o.startDate = DPGlobal.parseDate(o.startDate, format, o.language); - } - else { - o.startDate = -Infinity; - } - } - if (o.endDate !== Infinity){ - if (!!o.endDate){ - if (o.endDate instanceof Date) - o.endDate = this._local_to_utc(this._zero_time(o.endDate)); - else - o.endDate = DPGlobal.parseDate(o.endDate, format, o.language); - } - else { - o.endDate = Infinity; - } - } - - o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; - if (!$.isArray(o.daysOfWeekDisabled)) - o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); - o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){ - return parseInt(d, 10); - }); - - o.daysOfWeekHighlighted = o.daysOfWeekHighlighted||[]; - if (!$.isArray(o.daysOfWeekHighlighted)) - o.daysOfWeekHighlighted = o.daysOfWeekHighlighted.split(/[,\s]*/); - o.daysOfWeekHighlighted = $.map(o.daysOfWeekHighlighted, function(d){ - return parseInt(d, 10); - }); - - o.datesDisabled = o.datesDisabled||[]; - if (!$.isArray(o.datesDisabled)) { - var datesDisabled = []; - datesDisabled.push(DPGlobal.parseDate(o.datesDisabled, format, o.language)); - o.datesDisabled = datesDisabled; - } - o.datesDisabled = $.map(o.datesDisabled,function(d){ - return DPGlobal.parseDate(d, format, o.language); - }); - - var plc = String(o.orientation).toLowerCase().split(/\s+/g), - _plc = o.orientation.toLowerCase(); - plc = $.grep(plc, function(word){ - return /^auto|left|right|top|bottom$/.test(word); - }); - o.orientation = {x: 'auto', y: 'auto'}; - if (!_plc || _plc === 'auto') - ; // no action - else if (plc.length === 1){ - switch (plc[0]){ - case 'top': - case 'bottom': - o.orientation.y = plc[0]; - break; - case 'left': - case 'right': - o.orientation.x = plc[0]; - break; - } - } - else { - _plc = $.grep(plc, function(word){ - return /^left|right$/.test(word); - }); - o.orientation.x = _plc[0] || 'auto'; - - _plc = $.grep(plc, function(word){ - return /^top|bottom$/.test(word); - }); - o.orientation.y = _plc[0] || 'auto'; - } - if (o.defaultViewDate) { - var year = o.defaultViewDate.year || new Date().getFullYear(); - var month = o.defaultViewDate.month || 0; - var day = o.defaultViewDate.day || 1; - o.defaultViewDate = UTCDate(year, month, day); - } else { - o.defaultViewDate = UTCToday(); - } - }, - _events: [], - _secondaryEvents: [], - _applyEvents: function(evs){ - for (var i=0, el, ch, ev; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.on(ev, ch); - } - }, - _unapplyEvents: function(evs){ - for (var i=0, el, ev, ch; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.off(ev, ch); - } - }, - _buildEvents: function(){ - var events = { - keyup: $.proxy(function(e){ - if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1) - this.update(); - }, this), - keydown: $.proxy(this.keydown, this), - paste: $.proxy(this.paste, this) - }; - - if (this.o.showOnFocus === true) { - events.focus = $.proxy(this.show, this); + + this.viewMode = this.o.startView + + if (this.o.calendarWeeks) + this.picker + .find('thead .datepicker-title, tfoot .today, tfoot .clear') + .attr('colspan', function(i, val) { + return parseInt(val) + 1 + }) + + this._allow_update = false + + this.setStartDate(this._o.startDate) + this.setEndDate(this._o.endDate) + this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled) + this.setDaysOfWeekHighlighted(this.o.daysOfWeekHighlighted) + this.setDatesDisabled(this.o.datesDisabled) + + this.fillDow() + this.fillMonths() + + this._allow_update = true + + this.update() + this.showMode() + + if (this.isInline) { + this.show() + } + } + + Datepicker.prototype = { + constructor: Datepicker, + + _process_options: function(opts) { + // Store raw options for reference + this._o = $.extend({}, this._o, opts) + // Processed options + const o = (this.o = $.extend({}, this._o)) + + // Check if "de-DE" style date is available, if not language should + // fallback to 2 letter code eg "de" + let lang = o.language + if (!dates[lang]) { + lang = lang.split('-')[0] + if (!dates[lang]) lang = defaults.language + } + o.language = lang + + switch (o.startView) { + case 2: + case 'decade': + o.startView = 2 + break + case 1: + case 'year': + o.startView = 1 + break + default: + o.startView = 0 + } + + switch (o.minViewMode) { + case 1: + case 'months': + o.minViewMode = 1 + break + case 2: + case 'years': + o.minViewMode = 2 + break + default: + o.minViewMode = 0 + } + + switch (o.maxViewMode) { + case 0: + case 'days': + o.maxViewMode = 0 + break + case 1: + case 'months': + o.maxViewMode = 1 + break + default: + o.maxViewMode = 2 + } + + o.startView = Math.min(o.startView, o.maxViewMode) + o.startView = Math.max(o.startView, o.minViewMode) + + // true, false, or Number > 0 + if (o.multidate !== true) { + o.multidate = Number(o.multidate) || false + if (o.multidate !== false) o.multidate = Math.max(0, o.multidate) + } + o.multidateSeparator = String(o.multidateSeparator) + + o.weekStart %= 7 + o.weekEnd = (o.weekStart + 6) % 7 + + const format = DPGlobal.parseFormat(o.format) + if (o.startDate !== -Infinity) { + if (o.startDate) { + if (o.startDate instanceof Date) + o.startDate = this._local_to_utc(this._zero_time(o.startDate)) + else o.startDate = DPGlobal.parseDate(o.startDate, format, o.language) + } else { + o.startDate = -Infinity + } + } + if (o.endDate !== Infinity) { + if (o.endDate) { + if (o.endDate instanceof Date) + o.endDate = this._local_to_utc(this._zero_time(o.endDate)) + else o.endDate = DPGlobal.parseDate(o.endDate, format, o.language) + } else { + o.endDate = Infinity + } + } + + o.daysOfWeekDisabled = o.daysOfWeekDisabled || [] + if (!$.isArray(o.daysOfWeekDisabled)) + o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/) + o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d) { + return parseInt(d, 10) + }) + + o.daysOfWeekHighlighted = o.daysOfWeekHighlighted || [] + if (!$.isArray(o.daysOfWeekHighlighted)) + o.daysOfWeekHighlighted = o.daysOfWeekHighlighted.split(/[,\s]*/) + o.daysOfWeekHighlighted = $.map(o.daysOfWeekHighlighted, function(d) { + return parseInt(d, 10) + }) + + o.datesDisabled = o.datesDisabled || [] + if (!$.isArray(o.datesDisabled)) { + const datesDisabled = [] + datesDisabled.push( + DPGlobal.parseDate(o.datesDisabled, format, o.language) + ) + o.datesDisabled = datesDisabled + } + o.datesDisabled = $.map(o.datesDisabled, function(d) { + return DPGlobal.parseDate(d, format, o.language) + }) + + let plc = String(o.orientation) + .toLowerCase() + .split(/\s+/g); + let _plc = o.orientation.toLowerCase() + plc = $.grep(plc, function(word) { + return /^auto|left|right|top|bottom$/.test(word) + }) + o.orientation = { x: 'auto', y: 'auto' } + if (!_plc || _plc === 'auto'); + else if (plc.length === 1) { + // no action + switch (plc[0]) { + case 'top': + case 'bottom': + o.orientation.y = plc[0] + break + case 'left': + case 'right': + o.orientation.x = plc[0] + break + } + } else { + _plc = $.grep(plc, function(word) { + return /^left|right$/.test(word) + }) + o.orientation.x = _plc[0] || 'auto' + + _plc = $.grep(plc, function(word) { + return /^top|bottom$/.test(word) + }) + o.orientation.y = _plc[0] || 'auto' + } + if (o.defaultViewDate) { + const year = o.defaultViewDate.year || new Date().getFullYear() + const month = o.defaultViewDate.month || 0 + const day = o.defaultViewDate.day || 1 + o.defaultViewDate = UTCDate(year, month, day) + } else { + o.defaultViewDate = UTCToday() + } + }, + _events: [], + _secondaryEvents: [], + _applyEvents: function(evs) { + for (var i = 0, el, ch, ev; i < evs.length; i++) { + el = evs[i][0] + if (evs[i].length === 2) { + ch = undefined + ev = evs[i][1] + } else if (evs[i].length === 3) { + ch = evs[i][1] + ev = evs[i][2] + } + el.on(ev, ch) + } + }, + _unapplyEvents: function(evs) { + for (var i = 0, el, ev, ch; i < evs.length; i++) { + el = evs[i][0] + if (evs[i].length === 2) { + ch = undefined + ev = evs[i][1] + } else if (evs[i].length === 3) { + ch = evs[i][1] + ev = evs[i][2] + } + el.off(ev, ch) + } + }, + _buildEvents: function() { + const events = { + keyup: $.proxy(function(e) { + if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1) + this.update() + }, this), + keydown: $.proxy(this.keydown, this), + paste: $.proxy(this.paste, this) + } + + if (this.o.showOnFocus === true) { + events.focus = $.proxy(this.show, this) + } + + if (this.isInput) { + // single input + this._events = [[this.element, events]] + } else if (this.component && this.hasInput) { + // component: input + button + this._events = [ + // For components that are not readonly, allow keyboard nav + [this.element.find('input'), events], + [ + this.component, + { + click: $.proxy(this.show, this) + } + ] + ] + } else if (this.element.is('div')) { + // inline datepicker + this.isInline = true + } else { + this._events = [ + [ + this.element, + { + click: $.proxy(this.show, this) } + ] + ] + } + this._events.push( + // Component: listen for blur on element descendants + [ + this.element, + '*', + { + blur: $.proxy(function(e) { + this._focused_from = e.target + }, this) + } + ], + // Input: listen for blur on element + [ + this.element, + { + blur: $.proxy(function(e) { + this._focused_from = e.target + }, this) + } + ] + ) + + if (this.o.immediateUpdates) { + // Trigger input updates immediately on changed year/month + this._events.push([ + this.element, + { + 'changeYear changeMonth': $.proxy(function(e) { + this.update(e.date) + }, this) + } + ]) + } + + this._secondaryEvents = [ + [ + this.picker, + { + click: $.proxy(this.click, this) + } + ], + [ + $(window), + { + resize: $.proxy(this.place, this) + } + ], + [ + $(document), + { + mousedown: $.proxy(function(e) { + // Clicked outside the datepicker, hide it + if ( + !( + this.element.is(e.target) || + this.element.find(e.target).length || + this.picker.is(e.target) || + this.picker.find(e.target).length || + this.picker.hasClass('datepicker-inline') + ) + ) { + this.hide() + } + }, this) + } + ] + ] + }, + _attachEvents: function() { + this._detachEvents() + this._applyEvents(this._events) + }, + _detachEvents: function() { + this._unapplyEvents(this._events) + }, + _attachSecondaryEvents: function() { + this._detachSecondaryEvents() + this._applyEvents(this._secondaryEvents) + }, + _detachSecondaryEvents: function() { + this._unapplyEvents(this._secondaryEvents) + }, + _trigger: function(event, altdate) { + const date = altdate || this.dates.get(-1); + const local_date = this._utc_to_local(date) + + this.element.trigger({ + type: event, + date: local_date, + dates: $.map(this.dates, this._utc_to_local), + format: $.proxy(function(ix, format) { + if (arguments.length === 0) { + ix = this.dates.length - 1 + format = this.o.format + } else if (typeof ix === 'string') { + format = ix + ix = this.dates.length - 1 + } + format = format || this.o.format + const date = this.dates.get(ix) + return DPGlobal.formatDate(date, format, this.o.language) + }, this) + }) + }, + + show: function() { + const element = this.component ? this.element.find('input') : this.element + if (element.attr('readonly') && this.o.enableOnReadonly === false) return + if (!this.isInline) this.picker.appendTo(this.o.container) + this.place() + this.picker.show() + this._attachSecondaryEvents() + this._trigger('show') + if ( + (window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && + this.o.disableTouchKeyboard + ) { + $(this.element).blur() + } + return this + }, + + hide: function() { + if (this.isInline) return this + if (!this.picker.is(':visible')) return this + this.focusDate = null + this.picker.hide().detach() + this._detachSecondaryEvents() + this.viewMode = this.o.startView + this.showMode() + + if ( + this.o.forceParse && + ((this.isInput && this.element.val()) || + (this.hasInput && this.element.find('input').val())) + ) + this.setValue() + this._trigger('hide') + return this + }, + + remove: function() { + this.hide() + this._detachEvents() + this._detachSecondaryEvents() + this.picker.remove() + delete this.element.data().datepicker + if (!this.isInput) { + delete this.element.data().date + } + return this + }, + + paste: function(evt) { + let dateString + if ( + evt.originalEvent.clipboardData && + evt.originalEvent.clipboardData.types && + $.inArray('text/plain', evt.originalEvent.clipboardData.types) !== -1 + ) { + dateString = evt.originalEvent.clipboardData.getData('text/plain') + } else if (window.clipboardData) { + dateString = window.clipboardData.getData('Text') + } else { + return + } + this.setDate(dateString) + this.update() + evt.preventDefault() + }, + + _utc_to_local: function(utc) { + return utc && new Date(utc.getTime() + utc.getTimezoneOffset() * 60000) + }, + _local_to_utc: function(local) { + return ( + local && new Date(local.getTime() - local.getTimezoneOffset() * 60000) + ) + }, + _zero_time: function(local) { + return ( + local && + new Date(local.getFullYear(), local.getMonth(), local.getDate()) + ) + }, + _zero_utc_time: function(utc) { + return ( + utc && + new Date( + Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate()) + ) + ) + }, + + getDates: function() { + return $.map(this.dates, this._utc_to_local) + }, + + getUTCDates: function() { + return $.map(this.dates, function(d) { + return new Date(d) + }) + }, + + getDate: function() { + return this._utc_to_local(this.getUTCDate()) + }, + + getUTCDate: function() { + const selected_date = this.dates.get(-1) + if (typeof selected_date !== 'undefined') { + return new Date(selected_date) + } else { + return null + } + }, + + clearDates: function() { + let element + if (this.isInput) { + element = this.element + } else if (this.component) { + element = this.element.find('input') + } + + if (element) { + element.val('') + } - if (this.isInput) { // single input - this._events = [ - [this.element, events] - ]; + this.update() + this._trigger('changeDate') + + if (this.o.autoclose) { + this.hide() + } + }, + setDates: function() { + const args = $.isArray(arguments[0]) ? arguments[0] : arguments + this.update.apply(this, args) + this._trigger('changeDate') + this.setValue() + return this + }, + + setUTCDates: function() { + const args = $.isArray(arguments[0]) ? arguments[0] : arguments + this.update.apply(this, $.map(args, this._utc_to_local)) + this._trigger('changeDate') + this.setValue() + return this + }, + + setDate: alias('setDates'), + setUTCDate: alias('setUTCDates'), + + setValue: function() { + const formatted = this.getFormattedDate() + if (!this.isInput) { + if (this.component) { + this.element.find('input').val(formatted) + } + } else { + this.element.val(formatted) + } + return this + }, + + getFormattedDate: function(format) { + if (format === undefined) format = this.o.format + + const lang = this.o.language + return $.map(this.dates, function(d) { + return DPGlobal.formatDate(d, format, lang) + }).join(this.o.multidateSeparator) + }, + + setStartDate: function(startDate) { + this._process_options({ startDate }) + this.update() + this.updateNavArrows() + return this + }, + + setEndDate: function(endDate) { + this._process_options({ endDate }) + this.update() + this.updateNavArrows() + return this + }, + + setDaysOfWeekDisabled: function(daysOfWeekDisabled) { + this._process_options({ daysOfWeekDisabled }) + this.update() + this.updateNavArrows() + return this + }, + + setDaysOfWeekHighlighted: function(daysOfWeekHighlighted) { + this._process_options({ daysOfWeekHighlighted }) + this.update() + return this + }, + + setDatesDisabled: function(datesDisabled) { + this._process_options({ datesDisabled }) + this.update() + this.updateNavArrows() + }, + + place: function() { + if (this.isInline) return this + const calendarWidth = this.picker.outerWidth(); + const calendarHeight = this.picker.outerHeight(); + const visualPadding = 10; + const container = $(this.o.container); + const windowWidth = container.width(); + const scrollTop = + this.o.container === 'body' + ? $(document).scrollTop() + : container.scrollTop(); + const appendOffset = container.offset() + + const parentsZindex = [] + this.element.parents().each(function() { + const itemZIndex = $(this).css('z-index') + if (itemZIndex !== 'auto' && itemZIndex !== 0) + parentsZindex.push(parseInt(itemZIndex)) + }) + const zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset + const offset = this.component + ? this.component.parent().offset() + : this.element.offset() + const height = this.component + ? this.component.outerHeight(true) + : this.element.outerHeight(false) + const width = this.component + ? this.component.outerWidth(true) + : this.element.outerWidth(false) + let left = offset.left - appendOffset.left; + let top = offset.top - appendOffset.top + + if (this.o.container !== 'body') { + top += scrollTop + } + + this.picker.removeClass( + 'datepicker-orient-top datepicker-orient-bottom ' + + 'datepicker-orient-right datepicker-orient-left' + ) + + if (this.o.orientation.x !== 'auto') { + this.picker.addClass('datepicker-orient-' + this.o.orientation.x) + if (this.o.orientation.x === 'right') left -= calendarWidth - width + } + // auto x orientation is best-placement: if it crosses a window + // edge, fudge it sideways + else { + if (offset.left < 0) { + // component is outside the window on the left side. Move it into visible range + this.picker.addClass('datepicker-orient-left') + left -= offset.left - visualPadding + } else if (left + calendarWidth > windowWidth) { + // the calendar passes the widow right edge. Align it to component right side + this.picker.addClass('datepicker-orient-right') + left += width - calendarWidth + } else { + // Default to left + this.picker.addClass('datepicker-orient-left') + } + } + + // auto y orientation is best-situation: top or bottom, no fudging, + // decision based on which shows more of the calendar + let yorient = this.o.orientation.y; + let top_overflow + if (yorient === 'auto') { + top_overflow = -scrollTop + top - calendarHeight + yorient = top_overflow < 0 ? 'bottom' : 'top' + } + + this.picker.addClass('datepicker-orient-' + yorient) + if (yorient === 'top') + top -= calendarHeight + parseInt(this.picker.css('padding-top')) + else top += height + + if (this.o.rtl) { + const right = windowWidth - (left + width) + this.picker.css({ + top, + right, + zIndex + }) + } else { + this.picker.css({ + top, + left, + zIndex + }) + } + return this + }, + + _allow_update: true, + update: function() { + if (!this._allow_update) return this + + const oldDates = this.dates.copy(); + let dates = []; + let fromArgs = false + if (arguments.length) { + $.each( + arguments, + $.proxy(function(i, date) { + if (date instanceof Date) date = this._local_to_utc(date) + dates.push(date) + }, this) + ) + fromArgs = true + } else { + dates = this.isInput + ? this.element.val() + : this.element.data('date') || this.element.find('input').val() + if (dates && this.o.multidate) + dates = dates.split(this.o.multidateSeparator) + else dates = [dates] + delete this.element.data().date + } + + dates = $.map( + dates, + $.proxy(function(date) { + return DPGlobal.parseDate(date, this.o.format, this.o.language) + }, this) + ) + dates = $.grep( + dates, + $.proxy(function(date) { + return !this.dateWithinRange(date) || !date + }, this), + true + ) + this.dates.replace(dates) + + if (this.dates.length) this.viewDate = new Date(this.dates.get(-1)) + else if (this.viewDate < this.o.startDate) + this.viewDate = new Date(this.o.startDate) + else if (this.viewDate > this.o.endDate) + this.viewDate = new Date(this.o.endDate) + else this.viewDate = this.o.defaultViewDate + + if (fromArgs) { + // setting date by clicking + this.setValue() + } else if (dates.length) { + // setting date by typing + if (String(oldDates) !== String(this.dates)) this._trigger('changeDate') + } + if (!this.dates.length && oldDates.length) this._trigger('clearDate') + + this.fill() + this.element.change() + return this + }, + + fillDow: function() { + let dowCnt = this.o.weekStart; + let html = '' + if (this.o.calendarWeeks) { + this.picker + .find('.datepicker-days .datepicker-switch') + .attr('colspan', function(i, val) { + return parseInt(val) + 1 + }) + html += ' ' + } + while (dowCnt < this.o.weekStart + 7) { + html += + '' + + dates[this.o.language].daysMin[dowCnt++ % 7] + + '' + } + html += '' + this.picker.find('.datepicker-days thead').append(html) + }, + + fillMonths: function() { + let html = ''; + let i = 0 + while (i < 12) { + html += + '' + + dates[this.o.language].monthsShort[i++] + + '' + } + this.picker.find('.datepicker-months td').html(html) + }, + + setRange: function(range) { + if (!range || !range.length) delete this.range + else + this.range = $.map(range, function(d) { + return d.valueOf() + }) + this.fill() + }, + + getClassNames: function(date) { + const cls = []; + const year = this.viewDate.getUTCFullYear(); + const month = this.viewDate.getUTCMonth(); + const today = new Date() + if ( + date.getUTCFullYear() < year || + (date.getUTCFullYear() === year && date.getUTCMonth() < month) + ) { + cls.push('old') + } else if ( + date.getUTCFullYear() > year || + (date.getUTCFullYear() === year && date.getUTCMonth() > month) + ) { + cls.push('new') + } + if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) + cls.push('focused') + // Compare internal UTC date with local today, not UTC today + if ( + this.o.todayHighlight && + date.getUTCFullYear() === today.getFullYear() && + date.getUTCMonth() === today.getMonth() && + date.getUTCDate() === today.getDate() + ) { + cls.push('today') + } + if (this.dates.contains(date) !== -1) cls.push('active') + if (!this.dateWithinRange(date) || this.dateIsDisabled(date)) { + cls.push('disabled') + } + if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1) { + cls.push('highlighted') + } + + if (this.range) { + if (date > this.range[0] && date < this.range[this.range.length - 1]) { + cls.push('range') + } + if ($.inArray(date.valueOf(), this.range) !== -1) { + cls.push('selected') + } + if (date.valueOf() === this.range[0]) { + cls.push('range-start') + } + if (date.valueOf() === this.range[this.range.length - 1]) { + cls.push('range-end') + } + } + return cls + }, + + fill: function() { + const d = new Date(this.viewDate); + let year = d.getUTCFullYear(); + const month = d.getUTCMonth(); + const startYear = + this.o.startDate !== -Infinity + ? this.o.startDate.getUTCFullYear() + : -Infinity; + const startMonth = + this.o.startDate !== -Infinity + ? this.o.startDate.getUTCMonth() + : -Infinity; + const endYear = + this.o.endDate !== Infinity + ? this.o.endDate.getUTCFullYear() + : Infinity; + const endMonth = + this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity; + const todaytxt = dates[this.o.language].today || dates.en.today || ''; + const cleartxt = dates[this.o.language].clear || dates.en.clear || ''; + const titleFormat = + dates[this.o.language].titleFormat || dates.en.titleFormat; + let tooltip + if (isNaN(year) || isNaN(month)) return + this.picker + .find('.datepicker-days thead .datepicker-switch') + .text( + DPGlobal.formatDate( + new UTCDate(year, month), + titleFormat, + this.o.language + ) + ) + this.picker + .find('tfoot .today') + .text(todaytxt) + .toggle(this.o.todayBtn !== false) + this.picker + .find('tfoot .clear') + .text(cleartxt) + .toggle(this.o.clearBtn !== false) + this.picker + .find('thead .datepicker-title') + .text(this.o.title) + .toggle(this.o.title !== '') + this.updateNavArrows() + this.fillMonths() + const prevMonth = UTCDate(year, month - 1, 28); + const day = DPGlobal.getDaysInMonth( + prevMonth.getUTCFullYear(), + prevMonth.getUTCMonth() + ) + prevMonth.setUTCDate(day) + prevMonth.setUTCDate( + day - ((prevMonth.getUTCDay() - this.o.weekStart + 7) % 7) + ) + let nextMonth = new Date(prevMonth) + if (prevMonth.getUTCFullYear() < 100) { + nextMonth.setUTCFullYear(prevMonth.getUTCFullYear()) + } + nextMonth.setUTCDate(nextMonth.getUTCDate() + 42) + nextMonth = nextMonth.valueOf() + let html = [] + let clsName + while (prevMonth.valueOf() < nextMonth) { + if (prevMonth.getUTCDay() === this.o.weekStart) { + html.push('') + if (this.o.calendarWeeks) { + // ISO 8601: First week contains first thursday. + // ISO also states week starts on Monday, but we can be more abstract here. + const // Start of current week: based on weekstart/current date + ws = new Date( + +prevMonth + + ((this.o.weekStart - prevMonth.getUTCDay() - 7) % 7) * 864e5 + ); + // Thursday of this week + const th = new Date( + Number(ws) + ((7 + 4 - ws.getUTCDay()) % 7) * 864e5 + ); + // First Thursday of year, year from thursday + var yth = new Date( + Number((yth = UTCDate(th.getUTCFullYear(), 0, 1))) + + ((7 + 4 - yth.getUTCDay()) % 7) * 864e5 + ); + // Calendar week: ms between thursdays, div ms per day, div 7 days + const calWeek = (th - yth) / 864e5 / 7 + 1 + html.push('' + calWeek + '') + } + } + clsName = this.getClassNames(prevMonth) + clsName.push('day') + + if (this.o.beforeShowDay !== $.noop) { + let before = this.o.beforeShowDay(this._utc_to_local(prevMonth)) + if (before === undefined) before = {} + else if (typeof before === 'boolean') before = { enabled: before } + else if (typeof before === 'string') before = { classes: before } + if (before.enabled === false) clsName.push('disabled') + if (before.classes) + clsName = clsName.concat(before.classes.split(/\s+/)) + if (before.tooltip) tooltip = before.tooltip + } + + clsName = $.unique(clsName) + html.push( + '' + + prevMonth.getUTCDate() + + '' + ) + tooltip = null + if (prevMonth.getUTCDay() === this.o.weekEnd) { + html.push('') + } + prevMonth.setUTCDate(prevMonth.getUTCDate() + 1) + } + this.picker + .find('.datepicker-days tbody') + .empty() + .append(html.join('')) + + const monthsTitle = + dates[this.o.language].monthsTitle || + dates.en.monthsTitle || + 'Months' + const months = this.picker + .find('.datepicker-months') + .find('.datepicker-switch') + .text(this.o.maxViewMode < 2 ? monthsTitle : year) + .end() + .find('span') + .removeClass('active') + + $.each(this.dates, function(i, d) { + if (d.getUTCFullYear() === year) + months.eq(d.getUTCMonth()).addClass('active') + }) + + if (year < startYear || year > endYear) { + months.addClass('disabled') + } + if (year === startYear) { + months.slice(0, startMonth).addClass('disabled') + } + if (year === endYear) { + months.slice(endMonth + 1).addClass('disabled') + } + + if (this.o.beforeShowMonth !== $.noop) { + const that = this + $.each(months, function(i, month) { + if (!$(month).hasClass('disabled')) { + const moDate = new Date(year, i, 1) + const before = that.o.beforeShowMonth(moDate) + if (before === false) $(month).addClass('disabled') + } + }) + } + + html = '' + year = parseInt(year / 10, 10) * 10 + const yearCont = this.picker + .find('.datepicker-years') + .find('.datepicker-switch') + .text(year + '-' + (year + 9)) + .end() + .find('td') + year -= 1 + const years = $.map(this.dates, function(d) { + return d.getUTCFullYear() + }); + let classes + for (let i = -1; i < 11; i++) { + classes = ['year'] + tooltip = null + + if (i === -1) classes.push('old') + else if (i === 10) classes.push('new') + if ($.inArray(year, years) !== -1) classes.push('active') + if (year < startYear || year > endYear) classes.push('disabled') + + if (this.o.beforeShowYear !== $.noop) { + let yrBefore = this.o.beforeShowYear(new Date(year, 0, 1)) + if (yrBefore === undefined) yrBefore = {} + else if (typeof yrBefore === 'boolean') + yrBefore = { enabled: yrBefore } + else if (typeof yrBefore === 'string') + yrBefore = { classes: yrBefore } + if (yrBefore.enabled === false) classes.push('disabled') + if (yrBefore.classes) + classes = classes.concat(yrBefore.classes.split(/\s+/)) + if (yrBefore.tooltip) tooltip = yrBefore.tooltip + } + + html += + '' + + year + + '' + year += 1 + } + yearCont.html(html) + }, + + updateNavArrows: function() { + if (!this._allow_update) return + + const d = new Date(this.viewDate); + const year = d.getUTCFullYear(); + const month = d.getUTCMonth() + switch (this.viewMode) { + case 0: + if ( + this.o.startDate !== -Infinity && + year <= this.o.startDate.getUTCFullYear() && + month <= this.o.startDate.getUTCMonth() + ) { + this.picker.find('.prev').css({ visibility: 'hidden' }) + } else { + this.picker.find('.prev').css({ visibility: 'visible' }) + } + if ( + this.o.endDate !== Infinity && + year >= this.o.endDate.getUTCFullYear() && + month >= this.o.endDate.getUTCMonth() + ) { + this.picker.find('.next').css({ visibility: 'hidden' }) + } else { + this.picker.find('.next').css({ visibility: 'visible' }) + } + break + case 1: + case 2: + if ( + (this.o.startDate !== -Infinity && + year <= this.o.startDate.getUTCFullYear()) || + this.o.maxViewMode < 2 + ) { + this.picker.find('.prev').css({ visibility: 'hidden' }) + } else { + this.picker.find('.prev').css({ visibility: 'visible' }) + } + if ( + (this.o.endDate !== Infinity && + year >= this.o.endDate.getUTCFullYear()) || + this.o.maxViewMode < 2 + ) { + this.picker.find('.next').css({ visibility: 'hidden' }) + } else { + this.picker.find('.next').css({ visibility: 'visible' }) + } + break + } + }, + + click: function(e) { + e.preventDefault() + e.stopPropagation() + const target = $(e.target).closest('span, td, th'); + let year; + let month; + let day + if (target.length === 1) { + switch (target[0].nodeName.toLowerCase()) { + case 'th': + switch (target[0].className) { + case 'datepicker-switch': + this.showMode(1) + break + case 'prev': + case 'next': + var dir = + DPGlobal.modes[this.viewMode].navStep * + (target[0].className === 'prev' ? -1 : 1) + switch (this.viewMode) { + case 0: + this.viewDate = this.moveMonth(this.viewDate, dir) + this._trigger('changeMonth', this.viewDate) + break + case 1: + case 2: + this.viewDate = this.moveYear(this.viewDate, dir) + if (this.viewMode === 1) + this._trigger('changeYear', this.viewDate) + break + } + this.fill() + break + case 'today': + this.showMode(-2) + var which = this.o.todayBtn === 'linked' ? null : 'view' + this._setDate(UTCToday(), which) + break + case 'clear': + this.clearDates() + break + } + break + case 'span': + if (!target.hasClass('disabled')) { + this.viewDate.setUTCDate(1) + if (target.hasClass('month')) { + day = 1 + month = target + .parent() + .find('span') + .index(target) + year = this.viewDate.getUTCFullYear() + this.viewDate.setUTCMonth(month) + this._trigger('changeMonth', this.viewDate) + if (this.o.minViewMode === 1) { + this._setDate(UTCDate(year, month, day)) + this.showMode() + } else { + this.showMode(-1) + } + } else { + day = 1 + month = 0 + year = parseInt(target.text(), 10) || 0 + this.viewDate.setUTCFullYear(year) + this._trigger('changeYear', this.viewDate) + if (this.o.minViewMode === 2) { + this._setDate(UTCDate(year, month, day)) + } + this.showMode(-1) + } + this.fill() } - else if (this.component && this.hasInput) { // component: input + button - this._events = [ - // For components that are not readonly, allow keyboard nav - [this.element.find('input'), events], - [this.component, { - click: $.proxy(this.show, this) - }] - ]; + break + case 'td': + if (target.hasClass('day') && !target.hasClass('disabled')) { + day = parseInt(target.text(), 10) || 1 + year = this.viewDate.getUTCFullYear() + month = this.viewDate.getUTCMonth() + if (target.hasClass('old')) { + if (month === 0) { + month = 11 + year -= 1 + } else { + month -= 1 + } + } else if (target.hasClass('new')) { + if (month === 11) { + month = 0 + year += 1 + } else { + month += 1 + } + } + this._setDate(UTCDate(year, month, day)) } - else if (this.element.is('div')){ // inline datepicker - this.isInline = true; - } - else { - this._events = [ - [this.element, { - click: $.proxy(this.show, this) - }] - ]; - } - this._events.push( - // Component: listen for blur on element descendants - [this.element, '*', { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }], - // Input: listen for blur on element - [this.element, { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }] - ); - - if (this.o.immediateUpdates) { - // Trigger input updates immediately on changed year/month - this._events.push([this.element, { - 'changeYear changeMonth': $.proxy(function(e){ - this.update(e.date); - }, this) - }]); - } - - this._secondaryEvents = [ - [this.picker, { - click: $.proxy(this.click, this) - }], - [$(window), { - resize: $.proxy(this.place, this) - }], - [$(document), { - mousedown: $.proxy(function(e){ - // Clicked outside the datepicker, hide it - if (!( - this.element.is(e.target) || - this.element.find(e.target).length || - this.picker.is(e.target) || - this.picker.find(e.target).length || - this.picker.hasClass('datepicker-inline') - )){ - this.hide(); - } - }, this) - }] - ]; - }, - _attachEvents: function(){ - this._detachEvents(); - this._applyEvents(this._events); - }, - _detachEvents: function(){ - this._unapplyEvents(this._events); - }, - _attachSecondaryEvents: function(){ - this._detachSecondaryEvents(); - this._applyEvents(this._secondaryEvents); - }, - _detachSecondaryEvents: function(){ - this._unapplyEvents(this._secondaryEvents); - }, - _trigger: function(event, altdate){ - var date = altdate || this.dates.get(-1), - local_date = this._utc_to_local(date); - - this.element.trigger({ - type: event, - date: local_date, - dates: $.map(this.dates, this._utc_to_local), - format: $.proxy(function(ix, format){ - if (arguments.length === 0){ - ix = this.dates.length - 1; - format = this.o.format; - } - else if (typeof ix === 'string'){ - format = ix; - ix = this.dates.length - 1; - } - format = format || this.o.format; - var date = this.dates.get(ix); - return DPGlobal.formatDate(date, format, this.o.language); - }, this) - }); - }, - - show: function(){ - var element = this.component ? this.element.find('input') : this.element; - if (element.attr('readonly') && this.o.enableOnReadonly === false) - return; - if (!this.isInline) - this.picker.appendTo(this.o.container); - this.place(); - this.picker.show(); - this._attachSecondaryEvents(); - this._trigger('show'); - if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) { - $(this.element).blur(); - } - return this; - }, - - hide: function(){ - if (this.isInline) - return this; - if (!this.picker.is(':visible')) - return this; - this.focusDate = null; - this.picker.hide().detach(); - this._detachSecondaryEvents(); - this.viewMode = this.o.startView; - this.showMode(); - - if ( - this.o.forceParse && - ( - this.isInput && this.element.val() || - this.hasInput && this.element.find('input').val() - ) - ) - this.setValue(); - this._trigger('hide'); - return this; - }, - - remove: function(){ - this.hide(); - this._detachEvents(); - this._detachSecondaryEvents(); - this.picker.remove(); - delete this.element.data().datepicker; - if (!this.isInput){ - delete this.element.data().date; - } - return this; - }, - - paste: function(evt){ - var dateString; - if (evt.originalEvent.clipboardData && evt.originalEvent.clipboardData.types - && $.inArray('text/plain', evt.originalEvent.clipboardData.types) !== -1) { - dateString = evt.originalEvent.clipboardData.getData('text/plain'); - } - else if (window.clipboardData) { - dateString = window.clipboardData.getData('Text'); - } - else { - return; - } - this.setDate(dateString); - this.update(); - evt.preventDefault(); - }, - - _utc_to_local: function(utc){ - return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000)); - }, - _local_to_utc: function(local){ - return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000)); - }, - _zero_time: function(local){ - return local && new Date(local.getFullYear(), local.getMonth(), local.getDate()); - }, - _zero_utc_time: function(utc){ - return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate())); - }, - - getDates: function(){ - return $.map(this.dates, this._utc_to_local); - }, - - getUTCDates: function(){ - return $.map(this.dates, function(d){ - return new Date(d); - }); - }, - - getDate: function(){ - return this._utc_to_local(this.getUTCDate()); - }, - - getUTCDate: function(){ - var selected_date = this.dates.get(-1); - if (typeof selected_date !== 'undefined') { - return new Date(selected_date); - } else { - return null; - } - }, - - clearDates: function(){ - var element; - if (this.isInput) { - element = this.element; - } else if (this.component) { - element = this.element.find('input'); - } - - if (element) { - element.val(''); - } - - this.update(); - this._trigger('changeDate'); - - if (this.o.autoclose) { - this.hide(); - } - }, - setDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, args); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setUTCDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, $.map(args, this._utc_to_local)); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setDate: alias('setDates'), - setUTCDate: alias('setUTCDates'), - - setValue: function(){ - var formatted = this.getFormattedDate(); - if (!this.isInput){ - if (this.component){ - this.element.find('input').val(formatted); - } - } - else { - this.element.val(formatted); - } - return this; - }, - - getFormattedDate: function(format){ - if (format === undefined) - format = this.o.format; - - var lang = this.o.language; - return $.map(this.dates, function(d){ - return DPGlobal.formatDate(d, format, lang); - }).join(this.o.multidateSeparator); - }, - - setStartDate: function(startDate){ - this._process_options({startDate: startDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setEndDate: function(endDate){ - this._process_options({endDate: endDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDaysOfWeekDisabled: function(daysOfWeekDisabled){ - this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDaysOfWeekHighlighted: function(daysOfWeekHighlighted){ - this._process_options({daysOfWeekHighlighted: daysOfWeekHighlighted}); - this.update(); - return this; - }, - - setDatesDisabled: function(datesDisabled){ - this._process_options({datesDisabled: datesDisabled}); - this.update(); - this.updateNavArrows(); - }, - - place: function(){ - if (this.isInline) - return this; - var calendarWidth = this.picker.outerWidth(), - calendarHeight = this.picker.outerHeight(), - visualPadding = 10, - container = $(this.o.container), - windowWidth = container.width(), - scrollTop = this.o.container === 'body' ? $(document).scrollTop() : container.scrollTop(), - appendOffset = container.offset(); - - var parentsZindex = []; - this.element.parents().each(function(){ - var itemZIndex = $(this).css('z-index'); - if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex)); - }); - var zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset; - var offset = this.component ? this.component.parent().offset() : this.element.offset(); - var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false); - var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false); - var left = offset.left - appendOffset.left, - top = offset.top - appendOffset.top; - - if (this.o.container !== 'body') { - top += scrollTop; - } - - this.picker.removeClass( - 'datepicker-orient-top datepicker-orient-bottom '+ - 'datepicker-orient-right datepicker-orient-left' - ); - - if (this.o.orientation.x !== 'auto'){ - this.picker.addClass('datepicker-orient-' + this.o.orientation.x); - if (this.o.orientation.x === 'right') - left -= calendarWidth - width; - } - // auto x orientation is best-placement: if it crosses a window - // edge, fudge it sideways - else { - if (offset.left < 0) { - // component is outside the window on the left side. Move it into visible range - this.picker.addClass('datepicker-orient-left'); - left -= offset.left - visualPadding; - } else if (left + calendarWidth > windowWidth) { - // the calendar passes the widow right edge. Align it to component right side - this.picker.addClass('datepicker-orient-right'); - left += width - calendarWidth; - } else { - // Default to left - this.picker.addClass('datepicker-orient-left'); - } - } - - // auto y orientation is best-situation: top or bottom, no fudging, - // decision based on which shows more of the calendar - var yorient = this.o.orientation.y, - top_overflow; - if (yorient === 'auto'){ - top_overflow = -scrollTop + top - calendarHeight; - yorient = top_overflow < 0 ? 'bottom' : 'top'; - } - - this.picker.addClass('datepicker-orient-' + yorient); - if (yorient === 'top') - top -= calendarHeight + parseInt(this.picker.css('padding-top')); - else - top += height; - - if (this.o.rtl) { - var right = windowWidth - (left + width); - this.picker.css({ - top: top, - right: right, - zIndex: zIndex - }); - } else { - this.picker.css({ - top: top, - left: left, - zIndex: zIndex - }); - } - return this; - }, - - _allow_update: true, - update: function(){ - if (!this._allow_update) - return this; - - var oldDates = this.dates.copy(), - dates = [], - fromArgs = false; - if (arguments.length){ - $.each(arguments, $.proxy(function(i, date){ - if (date instanceof Date) - date = this._local_to_utc(date); - dates.push(date); - }, this)); - fromArgs = true; - } - else { - dates = this.isInput - ? this.element.val() - : this.element.data('date') || this.element.find('input').val(); - if (dates && this.o.multidate) - dates = dates.split(this.o.multidateSeparator); - else - dates = [dates]; - delete this.element.data().date; - } - - dates = $.map(dates, $.proxy(function(date){ - return DPGlobal.parseDate(date, this.o.format, this.o.language); - }, this)); - dates = $.grep(dates, $.proxy(function(date){ - return ( - !this.dateWithinRange(date) || - !date - ); - }, this), true); - this.dates.replace(dates); - - if (this.dates.length) - this.viewDate = new Date(this.dates.get(-1)); - else if (this.viewDate < this.o.startDate) - this.viewDate = new Date(this.o.startDate); - else if (this.viewDate > this.o.endDate) - this.viewDate = new Date(this.o.endDate); - else - this.viewDate = this.o.defaultViewDate; - - if (fromArgs){ - // setting date by clicking - this.setValue(); - } - else if (dates.length){ - // setting date by typing - if (String(oldDates) !== String(this.dates)) - this._trigger('changeDate'); - } - if (!this.dates.length && oldDates.length) - this._trigger('clearDate'); - - this.fill(); - this.element.change(); - return this; - }, - - fillDow: function(){ - var dowCnt = this.o.weekStart, - html = ''; - if (this.o.calendarWeeks){ - this.picker.find('.datepicker-days .datepicker-switch') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - html += ' '; - } - while (dowCnt < this.o.weekStart + 7){ - html += ''+dates[this.o.language].daysMin[(dowCnt++)%7]+''; - } - html += ''; - this.picker.find('.datepicker-days thead').append(html); - }, - - fillMonths: function(){ - var html = '', - i = 0; - while (i < 12){ - html += ''+dates[this.o.language].monthsShort[i++]+''; - } - this.picker.find('.datepicker-months td').html(html); - }, - - setRange: function(range){ - if (!range || !range.length) - delete this.range; - else - this.range = $.map(range, function(d){ - return d.valueOf(); - }); - this.fill(); - }, - - getClassNames: function(date){ - var cls = [], - year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(), - today = new Date(); - if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){ - cls.push('old'); - } - else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){ - cls.push('new'); - } - if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) - cls.push('focused'); - // Compare internal UTC date with local today, not UTC today - if (this.o.todayHighlight && - date.getUTCFullYear() === today.getFullYear() && - date.getUTCMonth() === today.getMonth() && - date.getUTCDate() === today.getDate()){ - cls.push('today'); - } - if (this.dates.contains(date) !== -1) - cls.push('active'); - if (!this.dateWithinRange(date) || this.dateIsDisabled(date)){ - cls.push('disabled'); - } - if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1){ - cls.push('highlighted'); - } - - if (this.range){ - if (date > this.range[0] && date < this.range[this.range.length-1]){ - cls.push('range'); - } - if ($.inArray(date.valueOf(), this.range) !== -1){ - cls.push('selected'); - } - if (date.valueOf() === this.range[0]){ - cls.push('range-start'); + break } - if (date.valueOf() === this.range[this.range.length-1]){ - cls.push('range-end'); + } + if (this.picker.is(':visible') && this._focused_from) { + $(this._focused_from).focus() + } + delete this._focused_from + }, + + _toggle_multidate: function(date) { + const ix = this.dates.contains(date) + if (!date) { + this.dates.clear() + } + + if (ix !== -1) { + if ( + this.o.multidate === true || + this.o.multidate > 1 || + this.o.toggleActive + ) { + this.dates.remove(ix) } - } - return cls; - }, - - fill: function(){ - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(), - startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, - startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, - endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, - endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, - todaytxt = dates[this.o.language].today || dates['en'].today || '', - cleartxt = dates[this.o.language].clear || dates['en'].clear || '', - titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat, - tooltip; - if (isNaN(year) || isNaN(month)) - return; - this.picker.find('.datepicker-days thead .datepicker-switch') - .text(DPGlobal.formatDate(new UTCDate(year, month), titleFormat, this.o.language)); - this.picker.find('tfoot .today') - .text(todaytxt) - .toggle(this.o.todayBtn !== false); - this.picker.find('tfoot .clear') - .text(cleartxt) - .toggle(this.o.clearBtn !== false); - this.picker.find('thead .datepicker-title') - .text(this.o.title) - .toggle(this.o.title !== ''); - this.updateNavArrows(); - this.fillMonths(); - var prevMonth = UTCDate(year, month-1, 28), - day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); - prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); - var nextMonth = new Date(prevMonth); - if (prevMonth.getUTCFullYear() < 100){ - nextMonth.setUTCFullYear(prevMonth.getUTCFullYear()); - } - nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); - nextMonth = nextMonth.valueOf(); - var html = []; - var clsName; - while (prevMonth.valueOf() < nextMonth){ - if (prevMonth.getUTCDay() === this.o.weekStart){ - html.push(''); - if (this.o.calendarWeeks){ - // ISO 8601: First week contains first thursday. - // ISO also states week starts on Monday, but we can be more abstract here. - var - // Start of current week: based on weekstart/current date - ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), - // Thursday of this week - th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), - // First Thursday of year, year from thursday - yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), - // Calendar week: ms between thursdays, div ms per day, div 7 days - calWeek = (th - yth) / 864e5 / 7 + 1; - html.push(''+ calWeek +''); - - } - } - clsName = this.getClassNames(prevMonth); - clsName.push('day'); - - if (this.o.beforeShowDay !== $.noop){ - var before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); - if (before === undefined) - before = {}; - else if (typeof(before) === 'boolean') - before = {enabled: before}; - else if (typeof(before) === 'string') - before = {classes: before}; - if (before.enabled === false) - clsName.push('disabled'); - if (before.classes) - clsName = clsName.concat(before.classes.split(/\s+/)); - if (before.tooltip) - tooltip = before.tooltip; - } - - clsName = $.unique(clsName); - html.push(''+prevMonth.getUTCDate() + ''); - tooltip = null; - if (prevMonth.getUTCDay() === this.o.weekEnd){ - html.push(''); - } - prevMonth.setUTCDate(prevMonth.getUTCDate()+1); - } - this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - - var monthsTitle = dates[this.o.language].monthsTitle || dates['en'].monthsTitle || 'Months'; - var months = this.picker.find('.datepicker-months') - .find('.datepicker-switch') - .text(this.o.maxViewMode < 2 ? monthsTitle : year) - .end() - .find('span').removeClass('active'); - - $.each(this.dates, function(i, d){ - if (d.getUTCFullYear() === year) - months.eq(d.getUTCMonth()).addClass('active'); - }); - - if (year < startYear || year > endYear){ - months.addClass('disabled'); - } - if (year === startYear){ - months.slice(0, startMonth).addClass('disabled'); - } - if (year === endYear){ - months.slice(endMonth+1).addClass('disabled'); - } - - if (this.o.beforeShowMonth !== $.noop){ - var that = this; - $.each(months, function(i, month){ - if (!$(month).hasClass('disabled')) { - var moDate = new Date(year, i, 1); - var before = that.o.beforeShowMonth(moDate); - if (before === false) - $(month).addClass('disabled'); - } - }); - } - - html = ''; - year = parseInt(year/10, 10) * 10; - var yearCont = this.picker.find('.datepicker-years') - .find('.datepicker-switch') - .text(year + '-' + (year + 9)) - .end() - .find('td'); - year -= 1; - var years = $.map(this.dates, function(d){ - return d.getUTCFullYear(); - }), - classes; - for (var i = -1; i < 11; i++){ - classes = ['year']; - tooltip = null; - - if (i === -1) - classes.push('old'); - else if (i === 10) - classes.push('new'); - if ($.inArray(year, years) !== -1) - classes.push('active'); - if (year < startYear || year > endYear) - classes.push('disabled'); - - if (this.o.beforeShowYear !== $.noop) { - var yrBefore = this.o.beforeShowYear(new Date(year, 0, 1)); - if (yrBefore === undefined) - yrBefore = {}; - else if (typeof(yrBefore) === 'boolean') - yrBefore = {enabled: yrBefore}; - else if (typeof(yrBefore) === 'string') - yrBefore = {classes: yrBefore}; - if (yrBefore.enabled === false) - classes.push('disabled'); - if (yrBefore.classes) - classes = classes.concat(yrBefore.classes.split(/\s+/)); - if (yrBefore.tooltip) - tooltip = yrBefore.tooltip; - } - - html += '' + year + ''; - year += 1; - } - yearCont.html(html); - }, - - updateNavArrows: function(){ - if (!this._allow_update) - return; - - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(); - switch (this.viewMode){ - case 0: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - case 1: - case 2: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() || this.o.maxViewMode < 2){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() || this.o.maxViewMode < 2){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - } - }, - - click: function(e){ - e.preventDefault(); - e.stopPropagation(); - var target = $(e.target).closest('span, td, th'), - year, month, day; - if (target.length === 1){ - switch (target[0].nodeName.toLowerCase()){ - case 'th': - switch (target[0].className){ - case 'datepicker-switch': - this.showMode(1); - break; - case 'prev': - case 'next': - var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); - switch (this.viewMode){ - case 0: - this.viewDate = this.moveMonth(this.viewDate, dir); - this._trigger('changeMonth', this.viewDate); - break; - case 1: - case 2: - this.viewDate = this.moveYear(this.viewDate, dir); - if (this.viewMode === 1) - this._trigger('changeYear', this.viewDate); - break; - } - this.fill(); - break; - case 'today': - this.showMode(-2); - var which = this.o.todayBtn === 'linked' ? null : 'view'; - this._setDate(UTCToday(), which); - break; - case 'clear': - this.clearDates(); - break; - } - break; - case 'span': - if (!target.hasClass('disabled')){ - this.viewDate.setUTCDate(1); - if (target.hasClass('month')){ - day = 1; - month = target.parent().find('span').index(target); - year = this.viewDate.getUTCFullYear(); - this.viewDate.setUTCMonth(month); - this._trigger('changeMonth', this.viewDate); - if (this.o.minViewMode === 1){ - this._setDate(UTCDate(year, month, day)); - this.showMode(); - } else { - this.showMode(-1); - } - } - else { - day = 1; - month = 0; - year = parseInt(target.text(), 10)||0; - this.viewDate.setUTCFullYear(year); - this._trigger('changeYear', this.viewDate); - if (this.o.minViewMode === 2){ - this._setDate(UTCDate(year, month, day)); - } - this.showMode(-1); - } - this.fill(); - } - break; - case 'td': - if (target.hasClass('day') && !target.hasClass('disabled')){ - day = parseInt(target.text(), 10)||1; - year = this.viewDate.getUTCFullYear(); - month = this.viewDate.getUTCMonth(); - if (target.hasClass('old')){ - if (month === 0){ - month = 11; - year -= 1; - } - else { - month -= 1; - } - } - else if (target.hasClass('new')){ - if (month === 11){ - month = 0; - year += 1; - } - else { - month += 1; - } - } - this._setDate(UTCDate(year, month, day)); - } - break; - } - } - if (this.picker.is(':visible') && this._focused_from){ - $(this._focused_from).focus(); - } - delete this._focused_from; - }, - - _toggle_multidate: function(date){ - var ix = this.dates.contains(date); - if (!date){ - this.dates.clear(); - } - - if (ix !== -1){ - if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){ - this.dates.remove(ix); - } - } else if (this.o.multidate === false) { - this.dates.clear(); - this.dates.push(date); - } - else { - this.dates.push(date); - } - - if (typeof this.o.multidate === 'number') - while (this.dates.length > this.o.multidate) - this.dates.remove(0); - }, - - _setDate: function(date, which){ - if (!which || which === 'date') - this._toggle_multidate(date && new Date(date)); - if (!which || which === 'view') - this.viewDate = date && new Date(date); - - this.fill(); - this.setValue(); - if (!which || which !== 'view') { - this._trigger('changeDate'); - } - var element; - if (this.isInput){ - element = this.element; - } - else if (this.component){ - element = this.element.find('input'); - } - if (element){ - element.change(); - } - if (this.o.autoclose && (!which || which === 'date')){ - this.hide(); - } - }, - - moveDay: function(date, dir){ - var newDate = new Date(date); - newDate.setUTCDate(date.getUTCDate() + dir); - - return newDate; - }, - - moveWeek: function(date, dir){ - return this.moveDay(date, dir * 7); - }, - - moveMonth: function(date, dir){ - if (!isValidDate(date)) - return this.o.defaultViewDate; - if (!dir) - return date; - var new_date = new Date(date.valueOf()), - day = new_date.getUTCDate(), - month = new_date.getUTCMonth(), - mag = Math.abs(dir), - new_month, test; - dir = dir > 0 ? 1 : -1; - if (mag === 1){ - test = dir === -1 - // If going back one month, make sure month is not current month - // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) - ? function(){ - return new_date.getUTCMonth() === month; - } - // If going forward one month, make sure month is as expected - // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) - : function(){ - return new_date.getUTCMonth() !== new_month; - }; - new_month = month + dir; - new_date.setUTCMonth(new_month); - // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 - if (new_month < 0 || new_month > 11) - new_month = (new_month + 12) % 12; - } - else { - // For magnitudes >1, move one month at a time... - for (var i=0; i < mag; i++) - // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... - new_date = this.moveMonth(new_date, dir); - // ...then reset the day, keeping it in the new month - new_month = new_date.getUTCMonth(); - new_date.setUTCDate(day); - test = function(){ - return new_month !== new_date.getUTCMonth(); - }; - } - // Common date-resetting loop -- if date is beyond end of month, make it - // end of month - while (test()){ - new_date.setUTCDate(--day); - new_date.setUTCMonth(new_month); - } - return new_date; - }, - - moveYear: function(date, dir){ - return this.moveMonth(date, dir*12); - }, - - moveAvailableDate: function(date, dir, fn){ - do { - date = this[fn](date, dir); - - if (!this.dateWithinRange(date)) - return false; - - fn = 'moveDay'; - } - while (this.dateIsDisabled(date)); - - return date; - }, - - weekOfDateIsDisabled: function(date){ - return $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1; - }, - - dateIsDisabled: function(date){ - return ( - this.weekOfDateIsDisabled(date) || - $.grep(this.o.datesDisabled, function(d){ - return isUTCEquals(date, d); - }).length > 0 - ); - }, - - dateWithinRange: function(date){ - return date >= this.o.startDate && date <= this.o.endDate; - }, - - keydown: function(e){ - if (!this.picker.is(':visible')){ - if (e.keyCode === 40 || e.keyCode === 27) { // allow down to re-show picker - this.show(); - e.stopPropagation(); + } else if (this.o.multidate === false) { + this.dates.clear() + this.dates.push(date) + } else { + this.dates.push(date) + } + + if (typeof this.o.multidate === 'number') + while (this.dates.length > this.o.multidate) this.dates.remove(0) + }, + + _setDate: function(date, which) { + if (!which || which === 'date') + this._toggle_multidate(date && new Date(date)) + if (!which || which === 'view') this.viewDate = date && new Date(date) + + this.fill() + this.setValue() + if (!which || which !== 'view') { + this._trigger('changeDate') + } + let element + if (this.isInput) { + element = this.element + } else if (this.component) { + element = this.element.find('input') + } + if (element) { + element.change() + } + if (this.o.autoclose && (!which || which === 'date')) { + this.hide() + } + }, + + moveDay: function(date, dir) { + const newDate = new Date(date) + newDate.setUTCDate(date.getUTCDate() + dir) + + return newDate + }, + + moveWeek: function(date, dir) { + return this.moveDay(date, dir * 7) + }, + + moveMonth: function(date, dir) { + if (!isValidDate(date)) return this.o.defaultViewDate + if (!dir) return date + let new_date = new Date(date.valueOf()); + let day = new_date.getUTCDate(); + const month = new_date.getUTCMonth(); + const mag = Math.abs(dir); + let new_month; + let test + dir = dir > 0 ? 1 : -1 + if (mag === 1) { + test = + dir === -1 + ? // If going back one month, make sure month is not current month + // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) + function() { + return new_date.getUTCMonth() === month + } + : // If going forward one month, make sure month is as expected + // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) + function() { + return new_date.getUTCMonth() !== new_month + } + new_month = month + dir + new_date.setUTCMonth(new_month) + // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 + if (new_month < 0 || new_month > 11) new_month = (new_month + 12) % 12 + } else { + // For magnitudes >1, move one month at a time... + for (let i = 0; i < mag; i++) + // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... + new_date = this.moveMonth(new_date, dir) + // ...then reset the day, keeping it in the new month + new_month = new_date.getUTCMonth() + new_date.setUTCDate(day) + test = function() { + return new_month !== new_date.getUTCMonth() + } + } + // Common date-resetting loop -- if date is beyond end of month, make it + // end of month + while (test()) { + new_date.setUTCDate(--day) + new_date.setUTCMonth(new_month) + } + return new_date + }, + + moveYear: function(date, dir) { + return this.moveMonth(date, dir * 12) + }, + + moveAvailableDate: function(date, dir, fn) { + do { + date = this[fn](date, dir) + + if (!this.dateWithinRange(date)) return false + + fn = 'moveDay' + } while (this.dateIsDisabled(date)) + + return date + }, + + weekOfDateIsDisabled: function(date) { + return $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1 + }, + + dateIsDisabled: function(date) { + return ( + this.weekOfDateIsDisabled(date) || + $.grep(this.o.datesDisabled, function(d) { + return isUTCEquals(date, d) + }).length > 0 + ) + }, + + dateWithinRange: function(date) { + return date >= this.o.startDate && date <= this.o.endDate + }, + + keydown: function(e) { + if (!this.picker.is(':visible')) { + if (e.keyCode === 40 || e.keyCode === 27) { + // allow down to re-show picker + this.show() + e.stopPropagation() + } + return + } + let dateChanged = false; + let dir; + let newViewDate; + let focusDate = this.focusDate || this.viewDate + switch (e.keyCode) { + case 27: // escape + if (this.focusDate) { + this.focusDate = null + this.viewDate = this.dates.get(-1) || this.viewDate + this.fill() + } else this.hide() + e.preventDefault() + e.stopPropagation() + break + case 37: // left + case 38: // up + case 39: // right + case 40: // down + if ( + !this.o.keyboardNavigation || + this.o.daysOfWeekDisabled.length === 7 + ) + break + dir = e.keyCode === 37 || e.keyCode === 38 ? -1 : 1 + if (e.ctrlKey) { + newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear') + + if (newViewDate) this._trigger('changeYear', this.viewDate) + } else if (e.shiftKey) { + newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth') + + if (newViewDate) this._trigger('changeMonth', this.viewDate) + } else if (e.keyCode === 37 || e.keyCode === 39) { + newViewDate = this.moveAvailableDate(focusDate, dir, 'moveDay') + } else if (!this.weekOfDateIsDisabled(focusDate)) { + newViewDate = this.moveAvailableDate(focusDate, dir, 'moveWeek') + } + if (newViewDate) { + this.focusDate = this.viewDate = newViewDate + this.setValue() + this.fill() + e.preventDefault() + } + break + case 13: // enter + if (!this.o.forceParse) break + focusDate = this.focusDate || this.dates.get(-1) || this.viewDate + if (this.o.keyboardNavigation) { + this._toggle_multidate(focusDate) + dateChanged = true + } + this.focusDate = null + this.viewDate = this.dates.get(-1) || this.viewDate + this.setValue() + this.fill() + if (this.picker.is(':visible')) { + e.preventDefault() + e.stopPropagation() + if (this.o.autoclose) this.hide() + } + break + case 9: // tab + this.focusDate = null + this.viewDate = this.dates.get(-1) || this.viewDate + this.fill() + this.hide() + break + } + if (dateChanged) { + if (this.dates.length) this._trigger('changeDate') + else this._trigger('clearDate') + let element + if (this.isInput) { + element = this.element + } else if (this.component) { + element = this.element.find('input') + } + if (element) { + element.change() + } + } + }, + + showMode: function(dir) { + if (dir) { + this.viewMode = Math.max( + this.o.minViewMode, + Math.min(this.o.maxViewMode, this.viewMode + dir) + ) + } + this.picker + .children('div') + .hide() + .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) + .show() + this.updateNavArrows() + } + } + + const DateRangePicker = function(element, options) { + $(element).data('datepicker', this) + this.element = $(element) + this.inputs = $.map(options.inputs, function(i) { + return i.jquery ? i[0] : i + }) + delete options.inputs + + datepickerPlugin + .call($(this.inputs), options) + .on('changeDate', $.proxy(this.dateUpdated, this)) + + this.pickers = $.map(this.inputs, function(i) { + return $(i).data('datepicker') + }) + this.updateDates() + } + DateRangePicker.prototype = { + updateDates: function() { + this.dates = $.map(this.pickers, function(i) { + return i.getUTCDate() + }) + this.updateRanges() + }, + updateRanges: function() { + const range = $.map(this.dates, function(d) { + return d.valueOf() + }) + $.each(this.pickers, function(i, p) { + p.setRange(range) + }) + }, + dateUpdated: function(e) { + // `this.updating` is a workaround for preventing infinite recursion + // between `changeDate` triggering and `setUTCDate` calling. Until + // there is a better mechanism. + if (this.updating) return + this.updating = true + + const dp = $(e.target).data('datepicker') + + if (typeof dp === 'undefined') { + return + } + + const new_date = dp.getUTCDate(); + const i = $.inArray(e.target, this.inputs); + let j = i - 1; + let k = i + 1; + const l = this.inputs.length + if (i === -1) return + + $.each(this.pickers, function(i, p) { + if (!p.getUTCDate()) p.setUTCDate(new_date) + }) + + if (new_date < this.dates[j]) { + // Date being moved earlier/left + while (j >= 0 && new_date < this.dates[j]) { + this.pickers[j--].setUTCDate(new_date) + } + } else if (new_date > this.dates[k]) { + // Date being moved later/right + while (k < l && new_date > this.dates[k]) { + this.pickers[k++].setUTCDate(new_date) + } + } + this.updateDates() + + delete this.updating + }, + remove: function() { + $.map(this.pickers, function(p) { + p.remove() + }) + delete this.element.data().datepicker + } + } + + function opts_from_el(el, prefix) { + // Derive options from element data-attrs + const data = $(el).data(); + const out = {}; + let inkey; + const replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])') + prefix = new RegExp('^' + prefix.toLowerCase()) + function re_lower(_, a) { + return a.toLowerCase() + } + for (const key in data) + if (prefix.test(key)) { + inkey = key.replace(replace, re_lower) + out[inkey] = data[key] + } + return out + } + + function opts_from_locale(lang) { + // Derive options from locale plugins + const out = {} + // Check if "de-DE" style date is available, if not language should + // fallback to 2 letter code eg "de" + if (!dates[lang]) { + lang = lang.split('-')[0] + if (!dates[lang]) return + } + const d = dates[lang] + $.each(locale_opts, function(i, k) { + if (k in d) out[k] = d[k] + }) + return out + } + + const old = $.fn.datepicker + var datepickerPlugin = function(option) { + const args = Array.apply(null, arguments) + args.shift() + let internal_return + this.each(function() { + const $this = $(this); + let data = $this.data('datepicker'); + const options = typeof option === 'object' && option + if (!data) { + const elopts = opts_from_el(this, 'date'); + // Preliminary otions + const xopts = $.extend({}, defaults, elopts, options); + const locopts = opts_from_locale(xopts.language); + // Options priority: js args, data-attrs, locales, defaults + const opts = $.extend({}, defaults, locopts, elopts, options) + if ($this.hasClass('input-daterange') || opts.inputs) { + $.extend(opts, { + inputs: opts.inputs || $this.find('input').toArray() + }) + data = new DateRangePicker(this, opts) + } else { + data = new Datepicker(this, opts) + } + $this.data('datepicker', data) + } + if (typeof option === 'string' && typeof data[option] === 'function') { + internal_return = data[option].apply(data, args) + } + }) + + if ( + internal_return === undefined || + internal_return instanceof Datepicker || + internal_return instanceof DateRangePicker + ) + return this + + if (this.length > 1) + throw new Error( + 'Using only allowed for the collection of a single element (' + + option + + ' function)' + ) + else return internal_return + } + $.fn.datepicker = datepickerPlugin + + var defaults = ($.fn.datepicker.defaults = { + autoclose: false, + beforeShowDay: $.noop, + beforeShowMonth: $.noop, + beforeShowYear: $.noop, + calendarWeeks: false, + clearBtn: false, + toggleActive: false, + daysOfWeekDisabled: [], + daysOfWeekHighlighted: [], + datesDisabled: [], + endDate: Infinity, + forceParse: true, + format: 'mm/dd/yyyy', + keyboardNavigation: true, + language: 'en', + minViewMode: 0, + maxViewMode: 2, + multidate: false, + multidateSeparator: ',', + orientation: 'auto', + rtl: false, + startDate: -Infinity, + startView: 0, + todayBtn: false, + todayHighlight: false, + weekStart: 0, + disableTouchKeyboard: false, + enableOnReadonly: true, + showOnFocus: true, + zIndexOffset: 10, + container: 'body', + immediateUpdates: false, + title: '' + }) + var locale_opts = ($.fn.datepicker.locale_opts = [ + 'format', + 'rtl', + 'weekStart' + ]) + $.fn.datepicker.Constructor = Datepicker + var dates = ($.fn.datepicker.dates = { + en: { + days: [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday' + ], + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + months: [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ], + monthsShort: [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' + ], + today: 'Today', + clear: 'Clear', + titleFormat: 'MM yyyy' + } + }) + + var DPGlobal = { + modes: [ + { + clsName: 'days', + navFnc: 'Month', + navStep: 1 + }, + { + clsName: 'months', + navFnc: 'FullYear', + navStep: 1 + }, + { + clsName: 'years', + navFnc: 'FullYear', + navStep: 10 + } + ], + isLeapYear: function(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 + }, + getDaysInMonth: function(year, month) { + return [ + 31, + DPGlobal.isLeapYear(year) ? 29 : 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ][month] + }, + validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, + nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, + parseFormat: function(format) { + if ( + typeof format.toValue === 'function' && + typeof format.toDisplay === 'function' + ) + return format + // IE treats \0 as a string end in inputs (truncating the value), + // so it's a bad format delimiter, anyway + const separators = format.replace(this.validParts, '\0').split('\0'); + const parts = format.match(this.validParts) + if (!separators || !separators.length || !parts || parts.length === 0) { + throw new Error('Invalid date format.') + } + return { separators, parts } + }, + parseDate: function(date, format, language) { + if (!date) return undefined + if (date instanceof Date) return date + if (typeof format === 'string') format = DPGlobal.parseFormat(format) + if (format.toValue) return format.toValue(date, format, language) + const part_re = /([\-+]\d+)([dmwy])/; + let parts = date.match(/([\-+]\d+)([dmwy])/g); + const fn_map = { + d: 'moveDay', + m: 'moveMonth', + w: 'moveWeek', + y: 'moveYear' + }; + let part; + let dir; + let i; + let fn + if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { + date = new Date() + for (i = 0; i < parts.length; i++) { + part = part_re.exec(parts[i]) + dir = parseInt(part[1]) + fn = fn_map[part[2]] + date = Datepicker.prototype[fn](date, dir) } - return; - } - var dateChanged = false, - dir, newViewDate, - focusDate = this.focusDate || this.viewDate; - switch (e.keyCode){ - case 27: // escape - if (this.focusDate){ - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - } - else - this.hide(); - e.preventDefault(); - e.stopPropagation(); - break; - case 37: // left - case 38: // up - case 39: // right - case 40: // down - if (!this.o.keyboardNavigation || this.o.daysOfWeekDisabled.length === 7) - break; - dir = e.keyCode === 37 || e.keyCode === 38 ? -1 : 1; - if (e.ctrlKey){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear'); - - if (newViewDate) - this._trigger('changeYear', this.viewDate); - } - else if (e.shiftKey){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth'); - - if (newViewDate) - this._trigger('changeMonth', this.viewDate); - } - else if (e.keyCode === 37 || e.keyCode === 39){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveDay'); - } - else if (!this.weekOfDateIsDisabled(focusDate)){ - newViewDate = this.moveAvailableDate(focusDate, dir, 'moveWeek'); - } - if (newViewDate){ - this.focusDate = this.viewDate = newViewDate; - this.setValue(); - this.fill(); - e.preventDefault(); - } - break; - case 13: // enter - if (!this.o.forceParse) - break; - focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; - if (this.o.keyboardNavigation) { - this._toggle_multidate(focusDate); - dateChanged = true; - } - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.setValue(); - this.fill(); - if (this.picker.is(':visible')){ - e.preventDefault(); - e.stopPropagation(); - if (this.o.autoclose) - this.hide(); - } - break; - case 9: // tab - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - this.hide(); - break; - } - if (dateChanged){ - if (this.dates.length) - this._trigger('changeDate'); - else - this._trigger('clearDate'); - var element; - if (this.isInput){ - element = this.element; - } - else if (this.component){ - element = this.element.find('input'); - } - if (element){ - element.change(); - } - } - }, - - showMode: function(dir){ - if (dir){ - this.viewMode = Math.max(this.o.minViewMode, Math.min(this.o.maxViewMode, this.viewMode + dir)); - } - this.picker - .children('div') - .hide() - .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) - .show(); - this.updateNavArrows(); - } - }; - - var DateRangePicker = function(element, options){ - $(element).data('datepicker', this); - this.element = $(element); - this.inputs = $.map(options.inputs, function(i){ - return i.jquery ? i[0] : i; - }); - delete options.inputs; - - datepickerPlugin.call($(this.inputs), options) - .on('changeDate', $.proxy(this.dateUpdated, this)); - - this.pickers = $.map(this.inputs, function(i){ - return $(i).data('datepicker'); - }); - this.updateDates(); - }; - DateRangePicker.prototype = { - updateDates: function(){ - this.dates = $.map(this.pickers, function(i){ - return i.getUTCDate(); - }); - this.updateRanges(); - }, - updateRanges: function(){ - var range = $.map(this.dates, function(d){ - return d.valueOf(); - }); - $.each(this.pickers, function(i, p){ - p.setRange(range); - }); - }, - dateUpdated: function(e){ - // `this.updating` is a workaround for preventing infinite recursion - // between `changeDate` triggering and `setUTCDate` calling. Until - // there is a better mechanism. - if (this.updating) - return; - this.updating = true; - - var dp = $(e.target).data('datepicker'); - - if (typeof(dp) === "undefined") { - return; - } - - var new_date = dp.getUTCDate(), - i = $.inArray(e.target, this.inputs), - j = i - 1, - k = i + 1, - l = this.inputs.length; - if (i === -1) - return; - - $.each(this.pickers, function(i, p){ - if (!p.getUTCDate()) - p.setUTCDate(new_date); - }); - - if (new_date < this.dates[j]){ - // Date being moved earlier/left - while (j >= 0 && new_date < this.dates[j]){ - this.pickers[j--].setUTCDate(new_date); - } - } - else if (new_date > this.dates[k]){ - // Date being moved later/right - while (k < l && new_date > this.dates[k]){ - this.pickers[k++].setUTCDate(new_date); - } - } - this.updateDates(); - - delete this.updating; - }, - remove: function(){ - $.map(this.pickers, function(p){ p.remove(); }); - delete this.element.data().datepicker; - } - }; - - function opts_from_el(el, prefix){ - // Derive options from element data-attrs - var data = $(el).data(), - out = {}, inkey, - replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); - prefix = new RegExp('^' + prefix.toLowerCase()); - function re_lower(_,a){ - return a.toLowerCase(); - } - for (var key in data) - if (prefix.test(key)){ - inkey = key.replace(replace, re_lower); - out[inkey] = data[key]; - } - return out; - } - - function opts_from_locale(lang){ - // Derive options from locale plugins - var out = {}; - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - return; - } - var d = dates[lang]; - $.each(locale_opts, function(i,k){ - if (k in d) - out[k] = d[k]; - }); - return out; - } - - var old = $.fn.datepicker; - var datepickerPlugin = function(option){ - var args = Array.apply(null, arguments); - args.shift(); - var internal_return; - this.each(function(){ - var $this = $(this), - data = $this.data('datepicker'), - options = typeof option === 'object' && option; - if (!data){ - var elopts = opts_from_el(this, 'date'), - // Preliminary otions - xopts = $.extend({}, defaults, elopts, options), - locopts = opts_from_locale(xopts.language), - // Options priority: js args, data-attrs, locales, defaults - opts = $.extend({}, defaults, locopts, elopts, options); - if ($this.hasClass('input-daterange') || opts.inputs){ - $.extend(opts, { - inputs: opts.inputs || $this.find('input').toArray() - }); - data = new DateRangePicker(this, opts); - } - else { - data = new Datepicker(this, opts); - } - $this.data('datepicker', data); - } - if (typeof option === 'string' && typeof data[option] === 'function'){ - internal_return = data[option].apply(data, args); - } - }); - - if ( - internal_return === undefined || - internal_return instanceof Datepicker || - internal_return instanceof DateRangePicker - ) - return this; - - if (this.length > 1) - throw new Error('Using only allowed for the collection of a single element (' + option + ' function)'); - else - return internal_return; - }; - $.fn.datepicker = datepickerPlugin; - - var defaults = $.fn.datepicker.defaults = { - autoclose: false, - beforeShowDay: $.noop, - beforeShowMonth: $.noop, - beforeShowYear: $.noop, - calendarWeeks: false, - clearBtn: false, - toggleActive: false, - daysOfWeekDisabled: [], - daysOfWeekHighlighted: [], - datesDisabled: [], - endDate: Infinity, - forceParse: true, - format: 'mm/dd/yyyy', - keyboardNavigation: true, - language: 'en', - minViewMode: 0, - maxViewMode: 2, - multidate: false, - multidateSeparator: ',', - orientation: "auto", - rtl: false, - startDate: -Infinity, - startView: 0, - todayBtn: false, - todayHighlight: false, - weekStart: 0, - disableTouchKeyboard: false, - enableOnReadonly: true, - showOnFocus: true, - zIndexOffset: 10, - container: 'body', - immediateUpdates: false, - title: '' - }; - var locale_opts = $.fn.datepicker.locale_opts = [ - 'format', - 'rtl', - 'weekStart' - ]; - $.fn.datepicker.Constructor = Datepicker; - var dates = $.fn.datepicker.dates = { - en: { - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today", - clear: "Clear", - titleFormat: "MM yyyy" - } - }; - - var DPGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'Month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'FullYear', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'FullYear', - navStep: 10 - }], - isLeapYear: function(year){ - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); - }, - getDaysInMonth: function(year, month){ - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; - }, - validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, - parseFormat: function(format){ - if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function') - return format; - // IE treats \0 as a string end in inputs (truncating the value), - // so it's a bad format delimiter, anyway - var separators = format.replace(this.validParts, '\0').split('\0'), - parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length === 0){ - throw new Error("Invalid date format."); - } - return {separators: separators, parts: parts}; - }, - parseDate: function(date, format, language){ - if (!date) - return undefined; - if (date instanceof Date) - return date; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - if (format.toValue) - return format.toValue(date, format, language); - var part_re = /([\-+]\d+)([dmwy])/, - parts = date.match(/([\-+]\d+)([dmwy])/g), - fn_map = { - d: 'moveDay', - m: 'moveMonth', - w: 'moveWeek', - y: 'moveYear' - }, - part, dir, i, fn; - if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ - date = new Date(); - for (i=0; i < parts.length; i++){ - part = part_re.exec(parts[i]); - dir = parseInt(part[1]); - fn = fn_map[part[2]]; - date = Datepicker.prototype[fn](date, dir); - } - return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()); - } - parts = date && date.match(this.nonpunctuation) || []; - date = new Date(); - var parsed = {}, - setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], - setters_map = { - yyyy: function(d,v){ - return d.setUTCFullYear(v); - }, - yy: function(d,v){ - return d.setUTCFullYear(2000+v); - }, - m: function(d,v){ - if (isNaN(d)) - return d; - v -= 1; - while (v < 0) v += 12; - v %= 12; - d.setUTCMonth(v); - while (d.getUTCMonth() !== v) - d.setUTCDate(d.getUTCDate()-1); - return d; - }, - d: function(d,v){ - return d.setUTCDate(v); - } - }, - val, filtered; - setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; - setters_map['dd'] = setters_map['d']; - date = UTCToday(); - var fparts = format.parts.slice(); - // Remove noop parts - if (parts.length !== fparts.length){ - fparts = $(fparts).filter(function(i,p){ - return $.inArray(p, setters_order) !== -1; - }).toArray(); - } - // Process remainder - function match_part(){ - var m = this.slice(0, parts[i].length), - p = parts[i].slice(0, m.length); - return m.toLowerCase() === p.toLowerCase(); - } - if (parts.length === fparts.length){ - var cnt; - for (i=0, cnt = fparts.length; i < cnt; i++){ - val = parseInt(parts[i], 10); - part = fparts[i]; - if (isNaN(val)){ - switch (part){ - case 'MM': - filtered = $(dates[language].months).filter(match_part); - val = $.inArray(filtered[0], dates[language].months) + 1; - break; - case 'M': - filtered = $(dates[language].monthsShort).filter(match_part); - val = $.inArray(filtered[0], dates[language].monthsShort) + 1; - break; - } - } - parsed[part] = val; - } - var _date, s; - for (i=0; i < setters_order.length; i++){ - s = setters_order[i]; - if (s in parsed && !isNaN(parsed[s])){ - _date = new Date(date); - setters_map[s](_date, parsed[s]); - if (!isNaN(_date)) - date = _date; - } - } - } - return date; - }, - formatDate: function(date, format, language){ - if (!date) - return ''; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - if (format.toDisplay) - return format.toDisplay(date, format, language); - var val = { - d: date.getUTCDate(), - D: dates[language].daysShort[date.getUTCDay()], - DD: dates[language].days[date.getUTCDay()], - m: date.getUTCMonth() + 1, - M: dates[language].monthsShort[date.getUTCMonth()], - MM: dates[language].months[date.getUTCMonth()], - yy: date.getUTCFullYear().toString().substring(2), - yyyy: date.getUTCFullYear() - }; - val.dd = (val.d < 10 ? '0' : '') + val.d; - val.mm = (val.m < 10 ? '0' : '') + val.m; - date = []; - var seps = $.extend([], format.separators); - for (var i=0, cnt = format.parts.length; i <= cnt; i++){ - if (seps.length) - date.push(seps.shift()); - date.push(val[format.parts[i]]); - } - return date.join(''); - }, - headTemplate: ''+ - ''+ - ''+ - ''+ - ''+ - '«'+ - ''+ - '»'+ - ''+ - '', - contTemplate: '', - footTemplate: ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - '' - }; - DPGlobal.template = '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - ''+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '; - - $.fn.datepicker.DPGlobal = DPGlobal; - - - /* DATEPICKER NO CONFLICT - * =================== */ - - $.fn.datepicker.noConflict = function(){ - $.fn.datepicker = old; - return this; - }; - - /* DATEPICKER VERSION - * =================== */ - $.fn.datepicker.version = '1.5.1'; - - /* DATEPICKER DATA-API - * ================== */ - - $(document).on( - 'focus.datepicker.data-api click.datepicker.data-api', - '[data-provide="datepicker"]', - function(e){ - var $this = $(this); - if ($this.data('datepicker')) - return; - e.preventDefault(); - // component click requires us to explicitly show it - datepickerPlugin.call($this, 'show'); - } - ); - $(function(){ - datepickerPlugin.call($('[data-provide="datepicker-inline"]')); - }); - -})); + return UTCDate( + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate() + ) + } + parts = (date && date.match(this.nonpunctuation)) || [] + date = new Date() + const parsed = {}; + const setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd']; + const setters_map = { + yyyy: function(d, v) { + return d.setUTCFullYear(v) + }, + yy: function(d, v) { + return d.setUTCFullYear(2000 + v) + }, + m: function(d, v) { + if (isNaN(d)) return d + v -= 1 + while (v < 0) v += 12 + v %= 12 + d.setUTCMonth(v) + while (d.getUTCMonth() !== v) d.setUTCDate(d.getUTCDate() - 1) + return d + }, + d: function(d, v) { + return d.setUTCDate(v) + } + }; + let val; + let filtered + setters_map.M = setters_map.MM = setters_map.mm = + setters_map.m + setters_map.dd = setters_map.d + date = UTCToday() + let fparts = format.parts.slice() + // Remove noop parts + if (parts.length !== fparts.length) { + fparts = $(fparts) + .filter(function(i, p) { + return $.inArray(p, setters_order) !== -1 + }) + .toArray() + } + // Process remainder + function match_part() { + const m = this.slice(0, parts[i].length); + const p = parts[i].slice(0, m.length) + return m.toLowerCase() === p.toLowerCase() + } + if (parts.length === fparts.length) { + let cnt + for (i = 0, cnt = fparts.length; i < cnt; i++) { + val = parseInt(parts[i], 10) + part = fparts[i] + if (isNaN(val)) { + switch (part) { + case 'MM': + filtered = $(dates[language].months).filter(match_part) + val = $.inArray(filtered[0], dates[language].months) + 1 + break + case 'M': + filtered = $(dates[language].monthsShort).filter(match_part) + val = $.inArray(filtered[0], dates[language].monthsShort) + 1 + break + } + } + parsed[part] = val + } + let _date, s + for (i = 0; i < setters_order.length; i++) { + s = setters_order[i] + if (s in parsed && !isNaN(parsed[s])) { + _date = new Date(date) + setters_map[s](_date, parsed[s]) + if (!isNaN(_date)) date = _date + } + } + } + return date + }, + formatDate: function(date, format, language) { + if (!date) return '' + if (typeof format === 'string') format = DPGlobal.parseFormat(format) + if (format.toDisplay) return format.toDisplay(date, format, language) + const val = { + d: date.getUTCDate(), + D: dates[language].daysShort[date.getUTCDay()], + DD: dates[language].days[date.getUTCDay()], + m: date.getUTCMonth() + 1, + M: dates[language].monthsShort[date.getUTCMonth()], + MM: dates[language].months[date.getUTCMonth()], + yy: date + .getUTCFullYear() + .toString() + .substring(2), + yyyy: date.getUTCFullYear() + } + val.dd = (val.d < 10 ? '0' : '') + val.d + val.mm = (val.m < 10 ? '0' : '') + val.m + date = [] + const seps = $.extend([], format.separators) + for (let i = 0, cnt = format.parts.length; i <= cnt; i++) { + if (seps.length) date.push(seps.shift()) + date.push(val[format.parts[i]]) + } + return date.join('') + }, + headTemplate: + '' + + '' + + '' + + '' + + '' + + '«' + + '' + + '»' + + '' + + '', + contTemplate: '', + footTemplate: + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + } + DPGlobal.template = + '
    ' + + '
    ' + + '' + + DPGlobal.headTemplate + + '' + + DPGlobal.footTemplate + + '
    ' + + '
    ' + + '
    ' + + '' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
    ' + + '
    ' + + '
    ' + + '' + + DPGlobal.headTemplate + + DPGlobal.contTemplate + + DPGlobal.footTemplate + + '
    ' + + '
    ' + + '
    ' + + $.fn.datepicker.DPGlobal = DPGlobal + + /* DATEPICKER NO CONFLICT + * =================== */ + + $.fn.datepicker.noConflict = function() { + $.fn.datepicker = old + return this + } + + /* DATEPICKER VERSION + * =================== */ + $.fn.datepicker.version = '1.5.1' + + /* DATEPICKER DATA-API + * ================== */ + + $(document).on( + 'focus.datepicker.data-api click.datepicker.data-api', + '[data-provide="datepicker"]', + function(e) { + const $this = $(this) + if ($this.data('datepicker')) return + e.preventDefault() + // component click requires us to explicitly show it + datepickerPlugin.call($this, 'show') + } + ) + $(function() { + datepickerPlugin.call($('[data-provide="datepicker-inline"]')) + }) +}) diff --git a/public/js/bootstrap.min.js b/public/js/bootstrap.min.js index c8f82e592..9864852dd 100644 --- a/public/js/bootstrap.min.js +++ b/public/js/bootstrap.min.js @@ -3,5 +3,1657 @@ * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a(document.body).height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file +if (typeof jQuery === 'undefined') + throw new Error("Bootstrap's JavaScript requires jQuery") +;+(function(a) { + 'use strict' + const b = a.fn.jquery.split(' ')[0].split('.') + if ((b[0] < 2 && b[1] < 9) || (b[0] == 1 && b[1] == 9 && b[2] < 1)) + throw new Error( + "Bootstrap's JavaScript requires jQuery version 1.9.1 or higher" + ) +})(jQuery), + +(function(a) { + 'use strict' + function b() { + const a = document.createElement('bootstrap'); + const b = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + OTransition: 'oTransitionEnd otransitionend', + transition: 'transitionend' + } + for (const c in b) if (void 0 !== a.style[c]) return { end: b[c] } + return !1 + } + ;(a.fn.emulateTransitionEnd = function(b) { + let c = !1; + const d = this + a(this).one('bsTransitionEnd', function() { + c = !0 + }) + const e = function() { + c || a(d).trigger(a.support.transition.end) + } + return setTimeout(e, b), this + }), + a(function() { + ;(a.support.transition = b()), + a.support.transition && + (a.event.special.bsTransitionEnd = { + bindType: a.support.transition.end, + delegateType: a.support.transition.end, + handle: function(b) { + return a(b.target).is(this) + ? b.handleObj.handler.apply(this, arguments) + : void 0 + } + }) + }) + })(jQuery), + +(function(a) { + 'use strict' + function b(b) { + return this.each(function() { + const c = a(this); + let e = c.data('bs.alert') + e || c.data('bs.alert', (e = new d(this))), + typeof b === 'string' && e[b].call(c) + }) + } + const c = '[data-dismiss="alert"]'; + var d = function(b) { + a(b).on('click', c, this.close) + } + ;(d.VERSION = '3.3.4'), + (d.TRANSITION_DURATION = 150), + (d.prototype.close = function(b) { + function c() { + g.detach() + .trigger('closed.bs.alert') + .remove() + } + const e = a(this); + let f = e.attr('data-target') + f || ((f = e.attr('href')), (f = f && f.replace(/.*(?=#[^\s]*$)/, ''))) + var g = a(f) + b && b.preventDefault(), + g.length || (g = e.closest('.alert')), + g.trigger((b = a.Event('close.bs.alert'))), + b.isDefaultPrevented() || + (g.removeClass('in'), + a.support.transition && g.hasClass('fade') + ? g + .one('bsTransitionEnd', c) + .emulateTransitionEnd(d.TRANSITION_DURATION) + : c()) + }) + const e = a.fn.alert + ;(a.fn.alert = b), + (a.fn.alert.Constructor = d), + (a.fn.alert.noConflict = function() { + return (a.fn.alert = e), this + }), + a(document).on('click.bs.alert.data-api', c, d.prototype.close) + })(jQuery), + +(function(a) { + 'use strict' + function b(b) { + return this.each(function() { + const d = a(this); + let e = d.data('bs.button'); + const f = typeof b === 'object' && b + e || d.data('bs.button', (e = new c(this, f))), + b == 'toggle' ? e.toggle() : b && e.setState(b) + }) + } + var c = function(b, d) { + ;(this.$element = a(b)), + (this.options = a.extend({}, c.DEFAULTS, d)), + (this.isLoading = !1) + } + ;(c.VERSION = '3.3.4'), + (c.DEFAULTS = { loadingText: 'loading...' }), + (c.prototype.setState = function(b) { + const c = 'disabled'; + const d = this.$element; + const e = d.is('input') ? 'val' : 'html'; + const f = d.data() + ;(b += 'Text'), + f.resetText == null && d.data('resetText', d[e]()), + setTimeout( + a.proxy(function() { + d[e](f[b] == null ? this.options[b] : f[b]), + b == 'loadingText' + ? ((this.isLoading = !0), d.addClass(c).attr(c, c)) + : this.isLoading && + ((this.isLoading = !1), d.removeClass(c).removeAttr(c)) + }, this), + 0 + ) + }), + (c.prototype.toggle = function() { + let a = !0; + const b = this.$element.closest('[data-toggle="buttons"]') + if (b.length) { + const c = this.$element.find('input') + c.prop('type') == 'radio' && + (c.prop('checked') && this.$element.hasClass('active') + ? (a = !1) + : b.find('.active').removeClass('active')), + a && + c + .prop('checked', !this.$element.hasClass('active')) + .trigger('change') + } else + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + a && this.$element.toggleClass('active') + }) + const d = a.fn.button + ;(a.fn.button = b), + (a.fn.button.Constructor = c), + (a.fn.button.noConflict = function() { + return (a.fn.button = d), this + }), + a(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function(c) { + let d = a(c.target) + d.hasClass('btn') || (d = d.closest('.btn')), + b.call(d, 'toggle'), + c.preventDefault() + }) + .on( + 'focus.bs.button.data-api blur.bs.button.data-api', + '[data-toggle^="button"]', + function(b) { + a(b.target) + .closest('.btn') + .toggleClass('focus', /^focus(in)?$/.test(b.type)) + } + ) + })(jQuery), + +(function(a) { + 'use strict' + function b(b) { + return this.each(function() { + const d = a(this); + let e = d.data('bs.carousel'); + const f = a.extend({}, c.DEFAULTS, d.data(), typeof b === 'object' && b); + const g = typeof b === 'string' ? b : f.slide + e || d.data('bs.carousel', (e = new c(this, f))), + typeof b === 'number' + ? e.to(b) + : g + ? e[g]() + : f.interval && e.pause().cycle() + }) + } + var c = function(b, c) { + ;(this.$element = a(b)), + (this.$indicators = this.$element.find('.carousel-indicators')), + (this.options = c), + (this.paused = null), + (this.sliding = null), + (this.interval = null), + (this.$active = null), + (this.$items = null), + this.options.keyboard && + this.$element.on('keydown.bs.carousel', a.proxy(this.keydown, this)), + this.options.pause == 'hover' && + !('ontouchstart' in document.documentElement) && + this.$element + .on('mouseenter.bs.carousel', a.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', a.proxy(this.cycle, this)) + } + ;(c.VERSION = '3.3.4'), + (c.TRANSITION_DURATION = 600), + (c.DEFAULTS = { interval: 5e3, pause: 'hover', wrap: !0, keyboard: !0 }), + (c.prototype.keydown = function(a) { + if (!/input|textarea/i.test(a.target.tagName)) { + switch (a.which) { + case 37: + this.prev() + break + case 39: + this.next() + break + default: + return + } + a.preventDefault() + } + }), + (c.prototype.cycle = function(b) { + return ( + b || (this.paused = !1), + this.interval && clearInterval(this.interval), + this.options.interval && + !this.paused && + (this.interval = setInterval( + a.proxy(this.next, this), + this.options.interval + )), + this + ) + }), + (c.prototype.getItemIndex = function(a) { + return ( + (this.$items = a.parent().children('.item')), + this.$items.index(a || this.$active) + ) + }), + (c.prototype.getItemForDirection = function(a, b) { + const c = this.getItemIndex(b); + const d = + (a == 'prev' && c === 0) || + (a == 'next' && c == this.$items.length - 1) + if (d && !this.options.wrap) return b + const e = a == 'prev' ? -1 : 1; + const f = (c + e) % this.$items.length + return this.$items.eq(f) + }), + (c.prototype.to = function(a) { + const b = this; + const c = this.getItemIndex( + (this.$active = this.$element.find('.item.active')) + ) + return a > this.$items.length - 1 || a < 0 + ? void 0 + : this.sliding + ? this.$element.one('slid.bs.carousel', function() { + b.to(a) + }) + : c == a + ? this.pause().cycle() + : this.slide(a > c ? 'next' : 'prev', this.$items.eq(a)) + }), + (c.prototype.pause = function(b) { + return ( + b || (this.paused = !0), + this.$element.find('.next, .prev').length && + a.support.transition && + (this.$element.trigger(a.support.transition.end), this.cycle(!0)), + (this.interval = clearInterval(this.interval)), + this + ) + }), + (c.prototype.next = function() { + return this.sliding ? void 0 : this.slide('next') + }), + (c.prototype.prev = function() { + return this.sliding ? void 0 : this.slide('prev') + }), + (c.prototype.slide = function(b, d) { + const e = this.$element.find('.item.active'); + const f = d || this.getItemForDirection(b, e); + const g = this.interval; + const h = b == 'next' ? 'left' : 'right'; + const i = this + if (f.hasClass('active')) return (this.sliding = !1) + const j = f[0]; + const k = a.Event('slide.bs.carousel', { relatedTarget: j, direction: h }) + if ((this.$element.trigger(k), !k.isDefaultPrevented())) { + if ( + ((this.sliding = !0), g && this.pause(), this.$indicators.length) + ) { + this.$indicators.find('.active').removeClass('active') + const l = a(this.$indicators.children()[this.getItemIndex(f)]) + l && l.addClass('active') + } + const m = a.Event('slid.bs.carousel', { + relatedTarget: j, + direction: h + }) + return ( + a.support.transition && this.$element.hasClass('slide') + ? (f.addClass(b), + f[0].offsetWidth, + e.addClass(h), + f.addClass(h), + e + .one('bsTransitionEnd', function() { + f.removeClass([b, h].join(' ')).addClass('active'), + e.removeClass(['active', h].join(' ')), + (i.sliding = !1), + setTimeout(function() { + i.$element.trigger(m) + }, 0) + }) + .emulateTransitionEnd(c.TRANSITION_DURATION)) + : (e.removeClass('active'), + f.addClass('active'), + (this.sliding = !1), + this.$element.trigger(m)), + g && this.cycle(), + this + ) + } + }) + const d = a.fn.carousel + ;(a.fn.carousel = b), + (a.fn.carousel.Constructor = c), + (a.fn.carousel.noConflict = function() { + return (a.fn.carousel = d), this + }) + const e = function(c) { + let d; + const e = a(this); + const f = a( + e.attr('data-target') || + ((d = e.attr('href')) && d.replace(/.*(?=#[^\s]+$)/, '')) + ) + if (f.hasClass('carousel')) { + const g = a.extend({}, f.data(), e.data()); + const h = e.attr('data-slide-to') + h && (g.interval = !1), + b.call(f, g), + h && f.data('bs.carousel').to(h), + c.preventDefault() + } + } + a(document) + .on('click.bs.carousel.data-api', '[data-slide]', e) + .on('click.bs.carousel.data-api', '[data-slide-to]', e), + a(window).on('load', function() { + a('[data-ride="carousel"]').each(function() { + const c = a(this) + b.call(c, c.data()) + }) + }) + })(jQuery), + +(function(a) { + 'use strict' + function b(b) { + let c; + const d = + b.attr('data-target') || + ((c = b.attr('href')) && c.replace(/.*(?=#[^\s]+$)/, '')) + return a(d) + } + function c(b) { + return this.each(function() { + const c = a(this); + let e = c.data('bs.collapse'); + const f = a.extend({}, d.DEFAULTS, c.data(), typeof b === 'object' && b) + !e && f.toggle && /show|hide/.test(b) && (f.toggle = !1), + e || c.data('bs.collapse', (e = new d(this, f))), + typeof b === 'string' && e[b]() + }) + } + var d = function(b, c) { + ;(this.$element = a(b)), + (this.options = a.extend({}, d.DEFAULTS, c)), + (this.$trigger = a( + '[data-toggle="collapse"][href="#' + + b.id + + '"],[data-toggle="collapse"][data-target="#' + + b.id + + '"]' + )), + (this.transitioning = null), + this.options.parent + ? (this.$parent = this.getParent()) + : this.addAriaAndCollapsedClass(this.$element, this.$trigger), + this.options.toggle && this.toggle() + } + ;(d.VERSION = '3.3.4'), + (d.TRANSITION_DURATION = 350), + (d.DEFAULTS = { toggle: !0 }), + (d.prototype.dimension = function() { + const a = this.$element.hasClass('width') + return a ? 'width' : 'height' + }), + (d.prototype.show = function() { + if (!this.transitioning && !this.$element.hasClass('in')) { + let b; + const e = + this.$parent && + this.$parent.children('.panel').children('.in, .collapsing') + if ( + !( + e && + e.length && + ((b = e.data('bs.collapse')), b && b.transitioning) + ) + ) { + const f = a.Event('show.bs.collapse') + if ((this.$element.trigger(f), !f.isDefaultPrevented())) { + e && + e.length && + (c.call(e, 'hide'), b || e.data('bs.collapse', null)) + const g = this.dimension() + this.$element + .removeClass('collapse') + .addClass('collapsing') + [g](0) + .attr('aria-expanded', !0), + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', !0), + (this.transitioning = 1) + const h = function() { + this.$element + .removeClass('collapsing') + .addClass('collapse in') + [g](''), + (this.transitioning = 0), + this.$element.trigger('shown.bs.collapse') + } + if (!a.support.transition) return h.call(this) + const i = a.camelCase(['scroll', g].join('-')) + this.$element + .one('bsTransitionEnd', a.proxy(h, this)) + .emulateTransitionEnd(d.TRANSITION_DURATION) + [g](this.$element[0][i]) + } + } + } + }), + (d.prototype.hide = function() { + if (!this.transitioning && this.$element.hasClass('in')) { + const b = a.Event('hide.bs.collapse') + if ((this.$element.trigger(b), !b.isDefaultPrevented())) { + const c = this.dimension() + this.$element[c](this.$element[c]())[0].offsetHeight, + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', !1), + this.$trigger.addClass('collapsed').attr('aria-expanded', !1), + (this.transitioning = 1) + const e = function() { + ;(this.transitioning = 0), + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + return a.support.transition + ? void this.$element[c](0) + .one('bsTransitionEnd', a.proxy(e, this)) + .emulateTransitionEnd(d.TRANSITION_DURATION) + : e.call(this) + } + } + }), + (d.prototype.toggle = function() { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + }), + (d.prototype.getParent = function() { + return a(this.options.parent) + .find( + '[data-toggle="collapse"][data-parent="' + + this.options.parent + + '"]' + ) + .each( + a.proxy(function(c, d) { + const e = a(d) + this.addAriaAndCollapsedClass(b(e), e) + }, this) + ) + .end() + }), + (d.prototype.addAriaAndCollapsedClass = function(a, b) { + const c = a.hasClass('in') + a.attr('aria-expanded', c), + b.toggleClass('collapsed', !c).attr('aria-expanded', c) + }) + const e = a.fn.collapse + ;(a.fn.collapse = c), + (a.fn.collapse.Constructor = d), + (a.fn.collapse.noConflict = function() { + return (a.fn.collapse = e), this + }), + a(document).on( + 'click.bs.collapse.data-api', + '[data-toggle="collapse"]', + function(d) { + const e = a(this) + e.attr('data-target') || d.preventDefault() + const f = b(e); + const g = f.data('bs.collapse'); + const h = g ? 'toggle' : e.data() + c.call(f, h) + } + ) + })(jQuery), + +(function(a) { + 'use strict' + function b(b) { + ;(b && b.which === 3) || + (a(e).remove(), + a(f).each(function() { + const d = a(this); + const e = c(d); + const f = { relatedTarget: this } + e.hasClass('open') && + (e.trigger((b = a.Event('hide.bs.dropdown', f))), + b.isDefaultPrevented() || + (d.attr('aria-expanded', 'false'), + e.removeClass('open').trigger('hidden.bs.dropdown', f))) + })) + } + function c(b) { + let c = b.attr('data-target') + c || + ((c = b.attr('href')), + (c = c && /#[A-Za-z]/.test(c) && c.replace(/.*(?=#[^\s]*$)/, ''))) + const d = c && a(c) + return d && d.length ? d : b.parent() + } + function d(b) { + return this.each(function() { + const c = a(this); + let d = c.data('bs.dropdown') + d || c.data('bs.dropdown', (d = new g(this))), + typeof b === 'string' && d[b].call(c) + }) + } + var e = '.dropdown-backdrop'; + var f = '[data-toggle="dropdown"]'; + var g = function(b) { + a(b).on('click.bs.dropdown', this.toggle) + } + ;(g.VERSION = '3.3.4'), + (g.prototype.toggle = function(d) { + const e = a(this) + if (!e.is('.disabled, :disabled')) { + const f = c(e); + const g = f.hasClass('open') + if ((b(), !g)) { + 'ontouchstart' in document.documentElement && + !f.closest('.navbar-nav').length && + a('