├── .gitignore ├── .vscode └── settings.json ├── src ├── common │ ├── constants.ts │ ├── Env.ts │ ├── route-errors.ts │ └── HttpStatusCodes.ts ├── util │ ├── misc.ts │ ├── validators.ts │ └── schema.ts ├── routes │ ├── common │ │ ├── Paths.ts │ │ └── index.ts │ ├── index.ts │ └── UserRoutes.ts ├── index.ts ├── repos │ ├── database.json │ ├── MockOrm.ts │ └── UserRepo.ts ├── models │ └── User.ts ├── public │ ├── scripts │ │ ├── http.js │ │ └── users.js │ ├── stylesheets │ │ └── users.css │ └── lib │ │ ├── bootstrap │ │ └── bootstrap.min.js │ │ └── handlebars │ │ └── handlebars.min.js ├── services │ └── UserService.ts ├── server.ts └── views │ └── users.html ├── spec ├── support │ ├── Paths.ts │ ├── jasmine.json │ └── index.ts ├── types │ ├── supertest │ │ └── index.d.ts │ └── misc.ts ├── index.ts └── tests │ └── users.spec.ts ├── config.ts ├── config ├── .env.test ├── .env.development └── .env.production ├── tsconfig.prod.json ├── tsconfig.json ├── scripts └── build.ts ├── README.md ├── package.json └── eslint.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | dist/ 3 | temp/ 4 | **/**/*.log 5 | 6 | config.js 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.options": { 3 | "flags": ["unstable_ts_config"] 4 | } 5 | } -------------------------------------------------------------------------------- /src/common/constants.ts: -------------------------------------------------------------------------------- 1 | export enum NodeEnvs { 2 | Dev = 'development', 3 | Test = 'test', 4 | Production = 'production' 5 | } -------------------------------------------------------------------------------- /spec/support/Paths.ts: -------------------------------------------------------------------------------- 1 | import jetPaths from 'jet-paths'; 2 | import Paths from '@src/routes/common/Paths'; 3 | 4 | export default jetPaths(Paths); 5 | -------------------------------------------------------------------------------- /src/util/misc.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Get a random number between 1 and 1,000,000,000,000 4 | */ 5 | export function getRandomInt(): number { 6 | return Math.floor(Math.random() * 1_000_000_000_000); 7 | } 8 | -------------------------------------------------------------------------------- /src/util/validators.ts: -------------------------------------------------------------------------------- 1 | import { isNumber } from 'jet-validators'; 2 | 3 | 4 | /** 5 | * Database relational key. 6 | */ 7 | export function isRelationalKey(arg: unknown): arg is number { 8 | return isNumber(arg) && arg >= -1; 9 | } -------------------------------------------------------------------------------- /src/routes/common/Paths.ts: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | Base: '/api', 4 | Users: { 5 | Base: '/users', 6 | Get: '/all', 7 | Add: '/add', 8 | Update: '/update', 9 | Delete: '/delete/:id', 10 | }, 11 | } as const; 12 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.ts" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": true 11 | } 12 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import logger from 'jet-logger'; 2 | import Env from '@src/common/Env'; 3 | import server from './server'; 4 | 5 | 6 | const SERVER_START_MSG = ('Express server started on port: ' + 7 | Env.Port.toString()); 8 | 9 | server.listen(Env.Port, () => logger.info(SERVER_START_MSG)); 10 | -------------------------------------------------------------------------------- /spec/types/supertest/index.d.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '@src/models/User'; 2 | import 'supertest'; 3 | 4 | 5 | declare module 'supertest' { 6 | 7 | export interface Response { 8 | headers: Record; 9 | body: { 10 | error: string, 11 | users: IUser[], 12 | }; 13 | } 14 | } -------------------------------------------------------------------------------- /src/repos/database.json: -------------------------------------------------------------------------------- 1 | {"users":[{"id":366115170645,"name":"Sean Maxwell","email":"smaxwell@example.com","created":"2024-03-22T05:14:36.252Z"},{"id":310946254456,"name":"John Smith","email":"john.smith@example.com","created":"2024-03-22T05:20:55.079Z"},{"id":143027113460,"name":"Gordan Freeman","email":"nova@prospect.com","created":"2024-03-22T05:42:18.895Z"}]} 2 | -------------------------------------------------------------------------------- /spec/types/misc.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'supertest'; 2 | import { IUser } from '@src/models/User'; 3 | 4 | 5 | // API Response 6 | export type TRes = Omit & { 7 | body: { 8 | error?: string, 9 | user?: IUser, 10 | users?: IUser[], 11 | }, 12 | }; 13 | 14 | // API Callback 15 | export type TApiCb = (res: TRes) => void; 16 | -------------------------------------------------------------------------------- /src/util/schema.ts: -------------------------------------------------------------------------------- 1 | import jetSchema from 'jet-schema'; 2 | import { isNumber, isString, isBoolean } from 'jet-validators'; 3 | import { isRelationalKey } from './validators'; 4 | 5 | 6 | export default jetSchema({ 7 | globals: [ 8 | { vf: isRelationalKey, default: -1 }, 9 | { vf: isNumber, default: 0 }, 10 | { vf: isString, default: '' }, 11 | { vf: isBoolean, default: false }, 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /src/common/Env.ts: -------------------------------------------------------------------------------- 1 | import jetEnv, { bool, num, str } from 'jet-env'; 2 | import { isEnumVal } from 'jet-validators'; 3 | 4 | import { NodeEnvs } from './constants'; 5 | 6 | 7 | export default jetEnv({ 8 | NodeEnv: isEnumVal(NodeEnvs), 9 | Port: num, 10 | Cookie: { 11 | Secret: str, 12 | Options: { 13 | httpOnly: bool, 14 | signed: bool, 15 | path: str, 16 | maxAge: num, 17 | domain: str, 18 | secure: bool, 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable n/no-process-env */ 2 | 3 | import path from 'path'; 4 | import dotenv from 'dotenv'; 5 | import moduleAlias from 'module-alias'; 6 | 7 | 8 | // Load environment files 9 | if (!!process.env.NODE_ENV) { 10 | const result = dotenv.config({ 11 | path: path.join(__dirname, `./config/.env.${process.env.NODE_ENV}`), 12 | }); 13 | if (!!result?.error) { 14 | throw result.error; 15 | } 16 | } 17 | 18 | // Configure moduleAlias 19 | if (__filename.endsWith('js')) { 20 | moduleAlias.addAlias('@src', __dirname + '/dist'); 21 | } 22 | -------------------------------------------------------------------------------- /config/.env.test: -------------------------------------------------------------------------------- 1 | ## Environment ## 2 | NODE_ENV=test 3 | 4 | 5 | ## Server ## 6 | PORT=4000 7 | HOST=localhost 8 | 9 | 10 | ## Setup jet-logger ## 11 | JET_LOGGER_MODE=CONSOLE 12 | JET_LOGGER_FILEPATH=jet-logger.log 13 | JET_LOGGER_TIMESTAMP=TRUE 14 | JET_LOGGER_FORMAT=LINE 15 | 16 | 17 | ## Authentication ## 18 | JWT_SECRET=asdf 19 | COOKIE_SECRET=asdf 20 | COOKIE_OPTIONS_SECURE=false 21 | COOKIE_OPTIONS_DOMAIN=localhost 22 | COOKIE_OPTIONS_PATH=/ 23 | COOKIE_OPTIONS_HTTP_ONLY=true 24 | COOKIE_OPTIONS_SIGNED=true 25 | # expires in 3 days 26 | COOKIE_OPTIONS_MAX_AGE=259200000 27 | -------------------------------------------------------------------------------- /config/.env.development: -------------------------------------------------------------------------------- 1 | ## Environment ## 2 | NODE_ENV=development 3 | 4 | 5 | ## Server ## 6 | PORT=3000 7 | HOST=localhost 8 | 9 | 10 | ## Setup jet-logger ## 11 | JET_LOGGER_MODE=CONSOLE 12 | JET_LOGGER_FILEPATH=jet-logger.log 13 | JET_LOGGER_TIMESTAMP=TRUE 14 | JET_LOGGER_FORMAT=LINE 15 | 16 | 17 | ## Authentication ## 18 | COOKIE_OPTIONS_DOMAIN=localhost 19 | COOKIE_OPTIONS_PATH=/ 20 | COOKIE_OPTIONS_SECURE=false 21 | JWT_SECRET=asdf 22 | COOKIE_SECRET=asdf 23 | COOKIE_OPTIONS_HTTP_ONLY=true 24 | COOKIE_OPTIONS_SIGNED=true 25 | # expires in 3 days 26 | COOKIE_OPTIONS_MAX_AGE=259200000 27 | -------------------------------------------------------------------------------- /spec/support/index.ts: -------------------------------------------------------------------------------- 1 | import { isValidDate } from 'jet-validators'; 2 | import { traverseObject } from 'jet-validators/utils'; 3 | 4 | 5 | /** 6 | * Send a date object over an API will convert it to a string, so for jasmine 7 | * tests to pass we need to convert them back to date objects. 8 | */ 9 | export function convertValidDates(toConvert: string | string[]) { 10 | if (!Array.isArray(toConvert)) { 11 | toConvert = [toConvert]; 12 | } 13 | return traverseObject((prop, value, parentObj) => { 14 | if (toConvert.includes(prop) && isValidDate(value)) { 15 | parentObj[prop] = new Date(value); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/models/User.ts: -------------------------------------------------------------------------------- 1 | import { isString } from 'jet-validators'; 2 | 3 | import schema from '@src/util/schema'; 4 | import { isRelationalKey } from '@src/util/validators'; 5 | 6 | 7 | export interface IUser { 8 | id: number; 9 | name: string; 10 | email: string; 11 | created: Date; 12 | address?: { street: string }; 13 | } 14 | 15 | const User = schema({ 16 | id: isRelationalKey, 17 | name: isString, 18 | created: Date, 19 | email: isString, 20 | address: schema({ 21 | street: isString, 22 | }, { optional: true }), 23 | }); 24 | 25 | 26 | // **** Export default **** // 27 | 28 | export default User; 29 | -------------------------------------------------------------------------------- /config/.env.production: -------------------------------------------------------------------------------- 1 | ## Environment ## 2 | NODE_ENV=production 3 | 4 | 5 | ## Server ## 6 | PORT=8081 7 | HOST=localhost 8 | 9 | 10 | ## Setup jet-logger ## 11 | JET_LOGGER_MODE=FILE 12 | JET_LOGGER_FILEPATH=jet-logger.log 13 | JET_LOGGER_TIMESTAMP=TRUE 14 | JET_LOGGER_FORMAT=LINE 15 | 16 | 17 | ## Authentication ## 18 | # SECURE_COOKIE 'false' here for demo-ing. But ideally should be true. 19 | ## Authentication ## 20 | JWT_SECRET=asdf 21 | COOKIE_SECRET=asdf 22 | COOKIE_OPTIONS_SECURE=false 23 | COOKIE_OPTIONS_DOMAIN=localhost 24 | COOKIE_OPTIONS_PATH=/ 25 | COOKIE_OPTIONS_HTTP_ONLY=true 26 | COOKIE_OPTIONS_SIGNED=true 27 | # expires in 3 days 28 | COOKIE_OPTIONS_MAX_AGE=259200000 29 | -------------------------------------------------------------------------------- /src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | import Paths from './common/Paths'; 4 | import UserRoutes from './UserRoutes'; 5 | 6 | 7 | // **** Variables **** // 8 | 9 | const apiRouter = Router(); 10 | 11 | 12 | // ** Add UserRouter ** // 13 | 14 | // Init router 15 | const userRouter = Router(); 16 | 17 | // Get all users 18 | userRouter.get(Paths.Users.Get, UserRoutes.getAll); 19 | userRouter.post(Paths.Users.Add, UserRoutes.add); 20 | userRouter.put(Paths.Users.Update, UserRoutes.update); 21 | userRouter.delete(Paths.Users.Delete, UserRoutes.delete); 22 | 23 | // Add UserRouter 24 | apiRouter.use(Paths.Users.Base, userRouter); 25 | 26 | 27 | // **** Export default **** // 28 | 29 | export default apiRouter; 30 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "CommonJS", 5 | "lib": ["ES2020"], 6 | "strict": true, 7 | "baseUrl": "./", 8 | "outDir": "temp", 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "paths": { 13 | "@src/*": ["src/*"] 14 | }, 15 | "useUnknownInCatchVariables": false, 16 | "sourceMap": false, 17 | "removeComments": true 18 | }, 19 | "include": [ 20 | "src/**/*.ts", 21 | "config.ts" 22 | ], 23 | "exclude": [ 24 | "spec", 25 | "src/public/", 26 | "scripts", 27 | "eslint.config.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/public/scripts/http.js: -------------------------------------------------------------------------------- 1 | var Http = (() => { 2 | 3 | // Setup request for json 4 | var getOptions = (verb, data) => { 5 | var options = { 6 | dataType: 'json', 7 | method: verb, 8 | headers: { 9 | 'Accept': 'application/json', 10 | 'Content-Type': 'application/json', 11 | }, 12 | }; 13 | if (!!data) { 14 | options.body = JSON.stringify(data); 15 | } 16 | return options; 17 | }; 18 | 19 | // Set Http methods 20 | return { 21 | get: (path) => fetch(path, getOptions('GET')), 22 | post: (path, data) => fetch(path, getOptions('POST', data)), 23 | put: (path, data) => fetch(path, getOptions('PUT', data)), 24 | delete: (path) => fetch(path, getOptions('DELETE')), 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /src/repos/MockOrm.ts: -------------------------------------------------------------------------------- 1 | import jsonfile from 'jsonfile'; 2 | import { IUser } from '@src/models/User'; 3 | 4 | 5 | // **** Variables **** // 6 | 7 | const DB_FILE_NAME = 'database.json'; 8 | 9 | 10 | // **** Types **** // 11 | 12 | interface IDb { 13 | users: IUser[]; 14 | } 15 | 16 | 17 | // **** Functions **** // 18 | 19 | /** 20 | * Fetch the json from the file. 21 | */ 22 | function openDb(): Promise { 23 | return jsonfile.readFile(__dirname + '/' + DB_FILE_NAME) as Promise; 24 | } 25 | 26 | /** 27 | * Update the file. 28 | */ 29 | function saveDb(db: IDb): Promise { 30 | return jsonfile.writeFile((__dirname + '/' + DB_FILE_NAME), db); 31 | } 32 | 33 | 34 | // **** Export default **** // 35 | 36 | export default { 37 | openDb, 38 | saveDb, 39 | } as const; 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "CommonJS", 5 | "lib": ["ES2020"], 6 | "strict": true, 7 | "baseUrl": "./", 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "paths": { 12 | "@src/*": ["src/*"] 13 | }, 14 | "useUnknownInCatchVariables": false 15 | }, 16 | "ts-node": { 17 | "swc": true, 18 | "require": [ 19 | "tsconfig-paths/register", 20 | "./config.ts" 21 | ], 22 | }, 23 | "include": [ 24 | "src/**/*.ts", 25 | "spec/**/*.ts", 26 | "config.ts", 27 | "scripts", 28 | "eslint.config.ts" 29 | ], 30 | "exclude": [ 31 | "src/public/*" 32 | ] 33 | } -------------------------------------------------------------------------------- /src/public/stylesheets/users.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 100px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | body .users-column { 7 | display: inline-block; 8 | margin-right: 2em; 9 | vertical-align: top; 10 | } 11 | 12 | body .users-column .column-header { 13 | padding-bottom: 5px; 14 | font-weight: 700; 15 | font-size: 1.2em; 16 | } 17 | 18 | 19 | /** Add User Column **/ 20 | 21 | body .add-user-col input { 22 | margin-bottom: 10px; 23 | } 24 | 25 | body .add-user-col #add-user-btn { 26 | margin-top: 2px; 27 | margin-bottom: 10px; 28 | } 29 | 30 | 31 | /** Users Display Column **/ 32 | 33 | .email-edit { 34 | padding-bottom: 8px; 35 | } 36 | 37 | body .users-column .user-display-ele { 38 | padding-bottom: 10px; 39 | } 40 | 41 | body .users-column .user-display-ele button { 42 | margin-top: 2px; 43 | margin-bottom: 10px; 44 | } 45 | 46 | body .users-column .user-display-ele .edit-view { 47 | display: none; 48 | } 49 | -------------------------------------------------------------------------------- /src/common/route-errors.ts: -------------------------------------------------------------------------------- 1 | import HttpStatusCodes from '@src/common/HttpStatusCodes'; 2 | 3 | 4 | export interface IValidationErrFormat { 5 | error: string; 6 | parameter: string; 7 | value?: unknown; 8 | 'more-info'?: string; 9 | } 10 | 11 | 12 | /** 13 | * Error with status code and message. 14 | */ 15 | export class RouteError extends Error { 16 | public status: HttpStatusCodes; 17 | 18 | public constructor(status: HttpStatusCodes, message: string) { 19 | super(message); 20 | this.status = status; 21 | } 22 | } 23 | 24 | /** 25 | * Validation in route layer errors. 26 | */ 27 | export class ValidationErr extends RouteError { 28 | public static MSG = 'The following parameter was missing or invalid.'; 29 | 30 | public constructor(parameter: string, value?: unknown, moreInfo?: string) { 31 | const msgObj: IValidationErrFormat = { 32 | error: ValidationErr.MSG, 33 | parameter, 34 | value, 35 | }; 36 | if (!!moreInfo) { 37 | msgObj['more-info'] = moreInfo; 38 | } 39 | super(HttpStatusCodes.BAD_REQUEST, JSON.stringify(msgObj)); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /spec/index.ts: -------------------------------------------------------------------------------- 1 | import find from 'find'; 2 | import Jasmine from 'jasmine'; 3 | import logger from 'jet-logger'; 4 | import { argv } from 'process'; 5 | 6 | 7 | // Init Jasmine 8 | const jasmine = new Jasmine(); 9 | jasmine.exitOnCompletion = false; 10 | 11 | // Set location of test files 12 | jasmine.loadConfig({ 13 | random: true, 14 | spec_dir: 'spec', 15 | spec_files: [ 16 | './tests/**/*.spec.ts', 17 | ], 18 | stopSpecOnExpectationFailure: false, 19 | }); 20 | 21 | // Run all or a single unit-test 22 | let execResp: Promise | undefined; 23 | if (!!argv[4]) { 24 | const testFile = argv[4]; 25 | find.file(testFile + '.spec.ts', './spec', (files: string[]) => { 26 | if (files.length === 1) { 27 | jasmine.execute([files[0]]); 28 | } else { 29 | logger.err('Test file not found!'); 30 | } 31 | }); 32 | } else { 33 | execResp = jasmine.execute(); 34 | } 35 | 36 | // Wait for tests to finish 37 | (async () => { 38 | if (!!execResp) { 39 | const info = await execResp; 40 | if (info.overallStatus === 'passed') { 41 | logger.info('All tests have passed :)'); 42 | } else { 43 | logger.err('At least one test has failed :('); 44 | } 45 | } 46 | })(); 47 | -------------------------------------------------------------------------------- /src/routes/common/index.ts: -------------------------------------------------------------------------------- 1 | import { Response, Request } from 'express'; 2 | import { isObject } from 'jet-validators'; 3 | import { parseObject, TSchema } from 'jet-validators/utils'; 4 | 5 | import { ValidationErr } from '@src/common/route-errors'; 6 | 7 | 8 | // **** Types **** // 9 | 10 | type TRecord = Record; 11 | export type IReq = Request; 12 | export type IRes = Response; 13 | 14 | 15 | // **** Functions **** // 16 | 17 | /** 18 | * Parse a Request object property and throw a Validation error if it fails. 19 | */ 20 | export function parseReq(schema: U) { 21 | const parseFn = parseObject(schema, _parseReqOnError); 22 | return (arg: unknown) => { 23 | if (isObject(arg)) { 24 | arg = { ...arg }; 25 | } 26 | return parseFn(arg); 27 | }; 28 | } 29 | /** 30 | * Error handler for the request parse function above. 31 | */ 32 | function _parseReqOnError( 33 | prop = 'undefined', 34 | value?: unknown, 35 | caughtErr?: unknown, 36 | ): void { 37 | if (caughtErr !== undefined) { 38 | const moreInfo = JSON.stringify(caughtErr); 39 | throw new ValidationErr(prop, value, moreInfo); 40 | } else { 41 | throw new ValidationErr(prop, value); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/services/UserService.ts: -------------------------------------------------------------------------------- 1 | import { RouteError } from '@src/common/route-errors'; 2 | import HttpStatusCodes from '@src/common/HttpStatusCodes'; 3 | 4 | import UserRepo from '@src/repos/UserRepo'; 5 | import { IUser } from '@src/models/User'; 6 | 7 | 8 | // **** Variables **** // 9 | 10 | export const USER_NOT_FOUND_ERR = 'User not found'; 11 | 12 | 13 | // **** Functions **** // 14 | 15 | /** 16 | * Get all users. 17 | */ 18 | function getAll(): Promise { 19 | return UserRepo.getAll(); 20 | } 21 | 22 | /** 23 | * Add one user. 24 | */ 25 | function addOne(user: IUser): Promise { 26 | return UserRepo.add(user); 27 | } 28 | 29 | /** 30 | * Update one user. 31 | */ 32 | async function updateOne(user: IUser): Promise { 33 | const persists = await UserRepo.persists(user.id); 34 | if (!persists) { 35 | throw new RouteError( 36 | HttpStatusCodes.NOT_FOUND, 37 | USER_NOT_FOUND_ERR, 38 | ); 39 | } 40 | return UserRepo.update(user); 41 | } 42 | 43 | /** 44 | * Delete a user by their id. 45 | */ 46 | async function _delete(id: number): Promise { 47 | const persists = await UserRepo.persists(id); 48 | if (!persists) { 49 | throw new RouteError( 50 | HttpStatusCodes.NOT_FOUND, 51 | USER_NOT_FOUND_ERR, 52 | ); 53 | } 54 | return UserRepo.delete(id); 55 | } 56 | 57 | 58 | // **** Export default **** // 59 | 60 | export default { 61 | getAll, 62 | addOne, 63 | updateOne, 64 | delete: _delete, 65 | } as const; 66 | -------------------------------------------------------------------------------- /src/routes/UserRoutes.ts: -------------------------------------------------------------------------------- 1 | import { isNumber } from 'jet-validators'; 2 | import { transform } from 'jet-validators/utils'; 3 | 4 | import HttpStatusCodes from '@src/common/HttpStatusCodes'; 5 | import UserService from '@src/services/UserService'; 6 | import User from '@src/models/User'; 7 | 8 | import { parseReq, IReq, IRes } from './common'; 9 | 10 | 11 | // **** Variables **** // 12 | 13 | const Validators = { 14 | add: parseReq({ user: User.test }), 15 | update: parseReq({ user: User.test }), 16 | delete: parseReq({ id: transform(Number, isNumber) }), 17 | } as const; 18 | 19 | 20 | // **** Functions **** // 21 | 22 | /** 23 | * Get all users. 24 | */ 25 | async function getAll(_: IReq, res: IRes) { 26 | const users = await UserService.getAll(); 27 | res.status(HttpStatusCodes.OK).json({ users }); 28 | } 29 | 30 | /** 31 | * Add one user. 32 | */ 33 | async function add(req: IReq, res: IRes) { 34 | const { user } = Validators.add(req.body); 35 | await UserService.addOne(user); 36 | res.status(HttpStatusCodes.CREATED).end(); 37 | } 38 | 39 | /** 40 | * Update one user. 41 | */ 42 | async function update(req: IReq, res: IRes) { 43 | const { user } = Validators.update(req.body); 44 | await UserService.updateOne(user); 45 | res.status(HttpStatusCodes.OK).end(); 46 | } 47 | 48 | /** 49 | * Delete one user. 50 | */ 51 | async function delete_(req: IReq, res: IRes) { 52 | const { id } = Validators.delete(req.params); 53 | await UserService.delete(id); 54 | res.status(HttpStatusCodes.OK).end(); 55 | } 56 | 57 | 58 | // **** Export default **** // 59 | 60 | export default { 61 | getAll, 62 | add, 63 | update, 64 | delete: delete_, 65 | } as const; 66 | -------------------------------------------------------------------------------- /scripts/build.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import logger from 'jet-logger'; 3 | import childProcess from 'child_process'; 4 | 5 | 6 | /** 7 | * Start 8 | */ 9 | (async () => { 10 | try { 11 | // Remove current build 12 | await remove('./dist/'); 13 | await exec('npm run lint', './'); 14 | await exec('tsc --build tsconfig.prod.json', './'); 15 | // Copy 16 | await copy('./src/public', './dist/public'); 17 | await copy('./src/views', './dist/views'); 18 | await copy('./src/repos/database.json', './dist/repos/database.json'); 19 | await copy('./temp/config.js', './config.js'); 20 | await copy('./temp/src', './dist'); 21 | await remove('./temp/'); 22 | } catch (err) { 23 | logger.err(err); 24 | // eslint-disable-next-line n/no-process-exit 25 | process.exit(1); 26 | } 27 | })(); 28 | 29 | /** 30 | * Remove file 31 | */ 32 | function remove(loc: string): Promise { 33 | return new Promise((res, rej) => { 34 | return fs.remove(loc, err => { 35 | return (!!err ? rej(err) : res()); 36 | }); 37 | }); 38 | } 39 | 40 | /** 41 | * Copy file. 42 | */ 43 | function copy(src: string, dest: string): Promise { 44 | return new Promise((res, rej) => { 45 | return fs.copy(src, dest, err => { 46 | return (!!err ? rej(err) : res()); 47 | }); 48 | }); 49 | } 50 | 51 | /** 52 | * Do command line command. 53 | */ 54 | function exec(cmd: string, loc: string): Promise { 55 | return new Promise((res, rej) => { 56 | return childProcess.exec(cmd, {cwd: loc}, (err, stdout, stderr) => { 57 | if (!!stdout) { 58 | logger.info(stdout); 59 | } 60 | if (!!stderr) { 61 | logger.warn(stderr); 62 | } 63 | return (!!err ? rej(err) : res()); 64 | }); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Express5 Typescript Template 2 | - Demo the latest version of ExpressJS (v5) with Typescript and the new "flat" configuration for ESlint. 3 | - Note that there is an additional git-branch that uses **tsx**: 4 | - `setup_using_tsx`: replace `nodemon, dotenv, swc, and ts-node` with tsx 5 | 6 | 7 | ## 📁 Background 8 | - Express and eslint have undergone some major updates recently so I want to do demo showing them together. Eslint follows a new file/format for configuring settings (which took me a while to setup) and express now comes packaged with typescript and "async error handling". 9 | - This project follows Typscript Best Practices listed here. 10 | 11 | 12 | ## 🛠️ Tools 13 | - Server: Express5 14 | - Linting: ESLint 15 | - Testing: Jamine + Supertest 16 | - Running typescript files: Ts-node 17 | - Hot Reloading: Nodemon, Swc/core 18 | - Environment variables: Dotenv and JetEnv 19 | - Logging: JetLogger 20 | - Schema validation: Jet-Schema 21 | - Dates: DayJs 22 | - Some front-end stuff: handlebars and bootstrap 23 | - Scan for unused exports: Knip 24 | - NOTE: `knip` tends to report a lot of false positives so use the output from `npm run clean:find-unused` as a guideline. 25 | 26 | 27 | ## ✅ Available Commands 28 | - Run development server: `npm run dev` 29 | - Ren development server with hot-reloading: `npm run dev:hot` 30 | - Run all unit-tests: `npm test` or `npm run test:hot` 31 | - Run a single unit-test: `npm test -- "name of test file" (i.e. users).` 32 | - Run linter: `npm run lint -- ./src` and `npm run lint -- ./src` 33 | - Check for type errors (tsx does not do this): `npm run type-check`. 34 | - Transpile production code: `npm run build` 35 | - Start production server: `npm start` 36 | 37 | 38 | ## ⚠️ Important 39 | 40 | In `src/server.ts` you're gonna wanna remove this on line 37: 41 | ```typescript 42 | { 43 | contentSecurityPolicy: false, 44 | } 45 | ``` 46 | 47 | This is not safe but I just did it to get the bootstrap and handlebars javascript files working in the front-end demo content. 48 | 49 | 50 | 🎉 Happy web deving! 51 | -------------------------------------------------------------------------------- /src/repos/UserRepo.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '@src/models/User'; 2 | import { getRandomInt } from '@src/util/misc'; 3 | import orm from './MockOrm'; 4 | 5 | 6 | // **** Functions **** // 7 | 8 | /** 9 | * Get one user. 10 | */ 11 | async function getOne(email: string): Promise { 12 | const db = await orm.openDb(); 13 | for (const user of db.users) { 14 | if (user.email === email) { 15 | return user; 16 | } 17 | } 18 | return null; 19 | } 20 | 21 | /** 22 | * See if a user with the given id exists. 23 | */ 24 | async function persists(id: number): Promise { 25 | const db = await orm.openDb(); 26 | for (const user of db.users) { 27 | if (user.id === id) { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | 34 | /** 35 | * Get all users. 36 | */ 37 | async function getAll(): Promise { 38 | const db = await orm.openDb(); 39 | return db.users; 40 | } 41 | 42 | /** 43 | * Add one user. 44 | */ 45 | async function add(user: IUser): Promise { 46 | const db = await orm.openDb(); 47 | user.id = getRandomInt(); 48 | db.users.push(user); 49 | return orm.saveDb(db); 50 | } 51 | 52 | /** 53 | * Update a user. 54 | */ 55 | async function update(user: IUser): Promise { 56 | const db = await orm.openDb(); 57 | for (let i = 0; i < db.users.length; i++) { 58 | if (db.users[i].id === user.id) { 59 | const dbUser = db.users[i]; 60 | db.users[i] = { 61 | ...dbUser, 62 | name: user.name, 63 | email: user.email, 64 | }; 65 | return orm.saveDb(db); 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * Delete one user. 72 | */ 73 | async function delete_(id: number): Promise { 74 | const db = await orm.openDb(); 75 | for (let i = 0; i < db.users.length; i++) { 76 | if (db.users[i].id === id) { 77 | db.users.splice(i, 1); 78 | return orm.saveDb(db); 79 | } 80 | } 81 | } 82 | 83 | 84 | // **** Export default **** // 85 | 86 | export default { 87 | getOne, 88 | persists, 89 | getAll, 90 | add, 91 | update, 92 | delete: delete_, 93 | } as const; 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express5-typescript-template", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "build": "ts-node ./scripts/build.ts", 6 | "clean:install": "rm -rf ./node_modules && rm -r package-lock.json && npm i", 7 | "clean:find-unused": "knip", 8 | "dev": "NODE_ENV=development ts-node ./src", 9 | "dev:hot": "nodemon --exec \"npm run dev\" --watch ./src --ext .ts", 10 | "lint": "eslint --flag unstable_ts_config", 11 | "start": "NODE_ENV=production node -r ./config.js ./dist", 12 | "test": "NODE_ENV=test ts-node ./spec", 13 | "test:hot": "nodemon --exec \"npm run test\" --watch ./src --watch ./spec --ext .ts", 14 | "type-check": "tsc --noEmit" 15 | }, 16 | "engines": { 17 | "node": ">=16.0.0" 18 | }, 19 | "knip": { 20 | "entry": [ 21 | "src/index.ts" 22 | ], 23 | "project": [ 24 | "**/*.ts" 25 | ] 26 | }, 27 | "dependencies": { 28 | "cookie-parser": "^1.4.6", 29 | "dayjs": "^1.11.13", 30 | "dotenv": "^16.4.5", 31 | "express": "^5.0.1", 32 | "helmet": "^8.0.0", 33 | "inserturlparams": "^2.0.5", 34 | "jet-env": "^1.1.4", 35 | "jet-logger": "^2.0.1", 36 | "jet-paths": "^1.1.0", 37 | "jet-schema": "^1.4.3", 38 | "jet-validators": "^1.0.9", 39 | "jsonfile": "^6.1.0", 40 | "module-alias": "^2.2.3", 41 | "morgan": "^1.10.0" 42 | }, 43 | "devDependencies": { 44 | "@eslint/js": "^9.11.1", 45 | "@stylistic/eslint-plugin-js": "^2.10.1", 46 | "@stylistic/eslint-plugin-ts": "^2.8.0", 47 | "@swc/core": "^1.7.39", 48 | "@types/cookie-parser": "^1.4.7", 49 | "@types/eslint__js": "^8.42.3", 50 | "@types/find": "^0.2.4", 51 | "@types/fs-extra": "^11.0.4", 52 | "@types/jasmine": "^5.1.4", 53 | "@types/jsonfile": "^6.1.4", 54 | "@types/module-alias": "^2.0.4", 55 | "@types/morgan": "^1.9.9", 56 | "@types/node": "^22.7.4", 57 | "@types/supertest": "^6.0.2", 58 | "eslint": "^9.11.1", 59 | "eslint-plugin-n": "^17.10.3", 60 | "find": "^0.3.0", 61 | "fs-extra": "^11.2.0", 62 | "jasmine": "^5.3.1", 63 | "jiti": "^2.3.3", 64 | "knip": "^5.36.3", 65 | "nodemon": "^3.1.7", 66 | "supertest": "^7.0.0", 67 | "ts-node": "^10.9.2", 68 | "tsconfig-paths": "^4.2.0", 69 | "typescript": "^5.6.2", 70 | "typescript-eslint": "^8.8.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import cookieParser from 'cookie-parser'; 2 | import morgan from 'morgan'; 3 | import path from 'path'; 4 | import helmet from 'helmet'; 5 | import express, { Request, Response, NextFunction } from 'express'; 6 | import logger from 'jet-logger'; 7 | 8 | import BaseRouter from '@src/routes'; 9 | 10 | import Paths from '@src/routes/common/Paths'; 11 | import Env from '@src/common/Env'; 12 | import HttpStatusCodes from '@src/common/HttpStatusCodes'; 13 | import { RouteError } from '@src/common/route-errors'; 14 | import { NodeEnvs } from '@src/common/constants'; 15 | 16 | 17 | // **** Variables **** // 18 | 19 | const app = express(); 20 | 21 | 22 | // **** Setup **** // 23 | 24 | // Basic middleware 25 | app.use(express.json()); 26 | app.use(express.urlencoded({ extended: true })); 27 | app.use(cookieParser(Env.Cookie.Secret)); 28 | 29 | // Show routes called in console during development 30 | if (Env.NodeEnv === NodeEnvs.Dev) { 31 | app.use(morgan('dev')); 32 | } 33 | 34 | // Security 35 | if (Env.NodeEnv === NodeEnvs.Production) { 36 | app.use(helmet({ 37 | contentSecurityPolicy: false, 38 | })); 39 | } 40 | 41 | // Add APIs, must be after middleware 42 | app.use(Paths.Base, BaseRouter); 43 | 44 | // Add error handler 45 | app.use((err: Error, _: Request, res: Response, next: NextFunction) => { 46 | if (Env.NodeEnv !== NodeEnvs.Test) { 47 | logger.err(err, true); 48 | } 49 | let status = HttpStatusCodes.BAD_REQUEST; 50 | if (err instanceof RouteError) { 51 | status = err.status; 52 | res.status(status).json({ error: err.message }); 53 | } 54 | return next(err); 55 | }); 56 | 57 | 58 | // **** Front-End Content **** // 59 | 60 | // Set views directory (html) 61 | const viewsDir = path.join(__dirname, 'views'); 62 | app.set('views', viewsDir); 63 | 64 | // Set static directory (js and css). 65 | const staticDir = path.join(__dirname, 'public'); 66 | app.use(express.static(staticDir)); 67 | 68 | // Nav to users pg by default 69 | app.get('/', (_: Request, res: Response) => { 70 | return res.redirect('/users'); 71 | }); 72 | 73 | // Redirect to login if not logged in. 74 | app.get('/users', (_: Request, res: Response) => { 75 | return res.sendFile('users.html', { root: viewsDir }); 76 | }); 77 | 78 | 79 | // **** Export default **** // 80 | 81 | export default app; 82 | -------------------------------------------------------------------------------- /eslint.config.ts: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import tseslint from 'typescript-eslint'; 3 | import stylisticJs from '@stylistic/eslint-plugin-js'; 4 | import stylisticTs from '@stylistic/eslint-plugin-ts'; 5 | import nodePlugin from 'eslint-plugin-n'; 6 | 7 | 8 | export default tseslint.config( 9 | eslint.configs.recommended, 10 | nodePlugin.configs['flat/recommended-script'], 11 | ...tseslint.configs.strictTypeChecked, 12 | ...tseslint.configs.stylisticTypeChecked, 13 | { 14 | ignores: [ 15 | '**/node_modules/*', 16 | '**/*.mjs', 17 | '**/*.js', 18 | ], 19 | }, 20 | { 21 | languageOptions: { 22 | parserOptions: { 23 | project: './tsconfig.json', 24 | warnOnUnsupportedTypeScriptVersion: false, 25 | }, 26 | }, 27 | }, 28 | { 29 | plugins: { 30 | '@stylistic/js': stylisticJs, 31 | '@stylistic/ts': stylisticTs, 32 | }, 33 | }, 34 | { 35 | files: ['**/*.ts'], 36 | }, 37 | { 38 | rules: { 39 | '@typescript-eslint/explicit-member-accessibility': 'warn', 40 | '@typescript-eslint/no-misused-promises': 0, 41 | '@typescript-eslint/no-floating-promises': 0, 42 | '@typescript-eslint/no-confusing-void-expression': 0, 43 | '@typescript-eslint/no-unnecessary-condition': 0, 44 | '@typescript-eslint/restrict-template-expressions': [ 45 | 'error', { allowNumber: true }, 46 | ], 47 | '@typescript-eslint/restrict-plus-operands': [ 48 | 'warn', { allowNumberAndString: true }, 49 | ], 50 | '@typescript-eslint/no-unused-vars': 'warn', 51 | '@typescript-eslint/no-unsafe-enum-comparison': 0, 52 | '@typescript-eslint/no-unnecessary-type-parameters': 0, 53 | '@stylistic/js/no-extra-semi': 'warn', 54 | 'max-len': [ 55 | 'warn', 56 | { 57 | 'code': 80, 58 | }, 59 | ], 60 | '@stylistic/ts/semi': ['warn', 'always'], 61 | '@stylistic/ts/member-delimiter-style': ['warn', { 62 | 'multiline': { 63 | 'delimiter': 'comma', 64 | 'requireLast': true, 65 | }, 66 | 'singleline': { 67 | 'delimiter': 'comma', 68 | 'requireLast': false, 69 | }, 70 | 'overrides': { 71 | 'interface': { 72 | 'singleline': { 73 | 'delimiter': 'semi', 74 | 'requireLast': false, 75 | }, 76 | 'multiline': { 77 | 'delimiter': 'semi', 78 | 'requireLast': true, 79 | }, 80 | }, 81 | }, 82 | }], 83 | '@typescript-eslint/no-non-null-assertion': 0, 84 | '@typescript-eslint/no-unused-expressions': 'warn', 85 | 'comma-dangle': ['warn', 'always-multiline'], 86 | 'no-console': 1, 87 | 'no-extra-boolean-cast': 0, 88 | 'indent': ['warn', 2], 89 | 'quotes': ['warn', 'single'], 90 | 'n/no-process-env': 1, 91 | 'n/no-missing-import': 0, 92 | 'n/no-unpublished-import': 0, 93 | 'prefer-const': 'warn', 94 | }, 95 | }, 96 | ); 97 | -------------------------------------------------------------------------------- /src/public/scripts/users.js: -------------------------------------------------------------------------------- 1 | // **** Variables **** // 2 | 3 | const DateFormatter = new Intl.DateTimeFormat('en-US', { 4 | year: 'numeric', 5 | month: '2-digit', 6 | day: '2-digit', 7 | }); 8 | 9 | const formatDate = (date) => DateFormatter.format(new Date(date)); 10 | 11 | 12 | // **** Run **** // 13 | 14 | // Start 15 | displayUsers(); 16 | 17 | /** 18 | * Call api 19 | */ 20 | function displayUsers() { 21 | Http 22 | .get('/api/users/all') 23 | .then(resp => resp.json()) 24 | .then(resp => { 25 | var allUsersTemplate = document.getElementById('all-users-template'), 26 | allUsersTemplateHtml = allUsersTemplate.innerHTML, 27 | template = Handlebars.compile(allUsersTemplateHtml); 28 | var allUsersAnchor = document.getElementById('all-users-anchor'); 29 | allUsersAnchor.innerHTML = template({ 30 | users: resp.users.map(user => ({ 31 | ...user, 32 | createdFormatted: formatDate(user.created), 33 | })), 34 | }); 35 | }); 36 | } 37 | 38 | // Setup event listener for button click 39 | document.addEventListener('click', event => { 40 | event.preventDefault(); 41 | var ele = event.target; 42 | if (ele.matches('#add-user-btn')) { 43 | addUser(); 44 | } else if (ele.matches('.edit-user-btn')) { 45 | showEditView(ele.parentNode.parentNode); 46 | } else if (ele.matches('.cancel-edit-btn')) { 47 | cancelEdit(ele.parentNode.parentNode); 48 | } else if (ele.matches('.submit-edit-btn')) { 49 | submitEdit(ele); 50 | } else if (ele.matches('.delete-user-btn')) { 51 | deleteUser(ele); 52 | } 53 | }, false); 54 | 55 | /** 56 | * Add a new user. 57 | */ 58 | function addUser() { 59 | var nameInput = document.getElementById('name-input'); 60 | var emailInput = document.getElementById('email-input'); 61 | var data = { 62 | user: { 63 | id: -1, 64 | name: nameInput.value, 65 | email: emailInput.value, 66 | created: new Date(), 67 | }, 68 | }; 69 | // Call api 70 | Http 71 | .post('/api/users/add', data) 72 | .then(() => { 73 | nameInput.value = ''; 74 | emailInput.value = ''; 75 | displayUsers(); 76 | }); 77 | } 78 | 79 | /** 80 | * Show edit view. 81 | */ 82 | function showEditView(userEle) { 83 | var normalView = userEle.getElementsByClassName('normal-view')[0]; 84 | var editView = userEle.getElementsByClassName('edit-view')[0]; 85 | normalView.style.display = 'none'; 86 | editView.style.display = 'block'; 87 | } 88 | 89 | /** 90 | * Cancel edit. 91 | */ 92 | function cancelEdit(userEle) { 93 | var normalView = userEle.getElementsByClassName('normal-view')[0]; 94 | var editView = userEle.getElementsByClassName('edit-view')[0]; 95 | normalView.style.display = 'block'; 96 | editView.style.display = 'none'; 97 | } 98 | 99 | /** 100 | * Submit edit. 101 | */ 102 | function submitEdit(ele) { 103 | var userEle = ele.parentNode.parentNode; 104 | var nameInput = userEle.getElementsByClassName('name-edit-input')[0]; 105 | var emailInput = userEle.getElementsByClassName('email-edit-input')[0]; 106 | var id = ele.getAttribute('data-user-id'); 107 | var created = ele.getAttribute('data-user-created'); 108 | console.log(ele, created) 109 | var data = { 110 | user: { 111 | id: Number(id), 112 | name: nameInput.value, 113 | email: emailInput.value, 114 | created: new Date(created), 115 | }, 116 | }; 117 | Http 118 | .put('/api/users/update', data) 119 | .then(() => displayUsers()); 120 | } 121 | 122 | /** 123 | * Delete a user 124 | */ 125 | function deleteUser(ele) { 126 | var id = ele.getAttribute('data-user-id'); 127 | Http 128 | .delete('/api/users/delete/' + id) 129 | .then(() => displayUsers()); 130 | } 131 | -------------------------------------------------------------------------------- /src/views/users.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Users 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | Add User: 27 |
28 |
29 | 35 |
36 |
37 | 43 |
44 |
45 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | Users: 58 |
59 | 60 |
61 | 120 |
121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /spec/tests/users.spec.ts: -------------------------------------------------------------------------------- 1 | import supertest, { Test } from 'supertest'; 2 | import TestAgent from 'supertest/lib/agent'; 3 | import insertUrlParams from 'inserturlparams'; 4 | import { parseJson } from 'jet-validators/utils'; 5 | import logger from 'jet-logger'; 6 | 7 | import app from '@src/server'; 8 | 9 | import UserRepo from '@src/repos/UserRepo'; 10 | import User, { IUser } from '@src/models/User'; 11 | import { USER_NOT_FOUND_ERR } from '@src/services/UserService'; 12 | 13 | import HttpStatusCodes from '@src/common/HttpStatusCodes'; 14 | import { IValidationErrFormat, ValidationErr } from '@src/common/route-errors'; 15 | 16 | import Paths from 'spec/support/Paths'; 17 | import { convertValidDates } from 'spec/support'; 18 | import { TApiCb, TRes } from 'spec/types/misc'; 19 | 20 | 21 | // **** Setup **** // 22 | 23 | // Dummy users for GET req 24 | const getDummyUsers = () => [ 25 | User.new({ name: 'Sean Maxwell', email: 'sean.maxwell@gmail.com' }), 26 | User.new({ name: 'John Smith', email: 'john.smith@gmail.com' }), 27 | User.new({ name: 'Gordan Freeman', email: 'gordan.freeman@gmail.com' }), 28 | ]; 29 | 30 | // Convert "created" prop back to Date object 31 | const createdKeyToDate = convertValidDates('created'); 32 | 33 | // Setup wrap-callback function 34 | const wrapCb = (cb: TApiCb) => (err: Error, res: TRes) => { 35 | if (!!err) { 36 | logger.err(err); 37 | } 38 | createdKeyToDate(res.body); 39 | return cb(res); 40 | }; 41 | 42 | 43 | // **** Run Tests **** // 44 | 45 | describe('UserRouter', () => { 46 | 47 | let agent: TestAgent; 48 | 49 | // Run before all tests 50 | beforeAll(done => { 51 | agent = supertest.agent(app); 52 | done(); 53 | }); 54 | 55 | // Get all users 56 | describe(`"GET:${Paths.Users.Get}"`, () => { 57 | 58 | // Setup API 59 | const api = (cb: TApiCb) => 60 | agent 61 | .get(Paths.Users.Get) 62 | .end(wrapCb(cb)); 63 | 64 | // Success 65 | it('should return a JSON object with all the users and a status code ' + 66 | `of "${HttpStatusCodes.OK}" if the request was successful.`, done => { 67 | // Add spy 68 | const data = getDummyUsers(); 69 | spyOn(UserRepo, 'getAll').and.resolveTo(data); 70 | // Call API 71 | api(res => { 72 | expect(res.status).toBe(HttpStatusCodes.OK); 73 | expect(res.body).toEqual({ users: data }); 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | // Test add user 80 | describe(`"POST:${Paths.Users.Add}"`, () => { 81 | 82 | const DUMMY_USER = getDummyUsers()[0]; 83 | 84 | // Setup API 85 | const callApi = (user: IUser | null, cb: TApiCb) => 86 | agent 87 | .post(Paths.Users.Add) 88 | .send({ user }) 89 | .end(wrapCb(cb)); 90 | 91 | // Test add user success 92 | it(`should return a status code of "${HttpStatusCodes.CREATED}" if the ` + 93 | 'request was successful.', done => { 94 | // Spy 95 | spyOn(UserRepo, 'add').and.resolveTo(); 96 | // Call api 97 | callApi(DUMMY_USER, res => { 98 | expect(res.status).toBe(HttpStatusCodes.CREATED); 99 | done(); 100 | }); 101 | }); 102 | 103 | // Missing param 104 | it('should return a JSON object with an error message of and a status ' + 105 | `code of "${HttpStatusCodes.BAD_REQUEST}" if the user param was ` + 106 | 'missing.', done => { 107 | // Call api 108 | callApi(null, res => { 109 | expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST); 110 | const errorObj = parseJson(res.body.error); 111 | expect(errorObj.error).toBe(ValidationErr.MSG); 112 | expect(errorObj.parameter).toBe('user'); 113 | done(); 114 | }); 115 | }); 116 | }); 117 | 118 | // Update users 119 | describe(`"PUT:${Paths.Users.Update}"`, () => { 120 | 121 | const DUMMY_USER = getDummyUsers()[0]; 122 | 123 | // Setup API 124 | const callApi = (user: IUser | null, cb: TApiCb) => 125 | agent 126 | .put(Paths.Users.Update) 127 | .send({ user }) 128 | .end(wrapCb(cb)); 129 | 130 | // Success 131 | it(`should return a status code of "${HttpStatusCodes.OK}" if the ` + 132 | 'request was successful.', done => { 133 | spyOn(UserRepo, 'update').and.resolveTo(); 134 | spyOn(UserRepo, 'persists').and.resolveTo(true); 135 | callApi(DUMMY_USER, res => { 136 | expect(res.status).toBe(HttpStatusCodes.OK); 137 | done(); 138 | }); 139 | }); 140 | 141 | // Param missing 142 | it('should return a JSON object with an error message and a status code ' + 143 | `of "${HttpStatusCodes.BAD_REQUEST}" if the user param was missing`, 144 | done => { 145 | callApi(null, res => { 146 | expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST); 147 | const errorObj = parseJson(res.body.error); 148 | expect(errorObj.error).toBe(ValidationErr.MSG); 149 | expect(errorObj.parameter).toBe('user'); 150 | done(); 151 | }); 152 | }); 153 | 154 | // User not found 155 | it('should return a JSON object with the error message of ' + 156 | `"${USER_NOT_FOUND_ERR}" and a status code of ` + 157 | `"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, done => { 158 | callApi(DUMMY_USER, res => { 159 | expect(res.status).toBe(HttpStatusCodes.NOT_FOUND); 160 | expect(res.body.error).toBe(USER_NOT_FOUND_ERR); 161 | done(); 162 | }); 163 | }); 164 | }); 165 | 166 | // Delete User 167 | describe(`"DELETE:${Paths.Users.Delete}"`, () => { 168 | 169 | // Call API 170 | const callApi = (id: number, cb: TApiCb) => 171 | agent 172 | .delete(insertUrlParams(Paths.Users.Delete, { id })) 173 | .end(wrapCb(cb)); 174 | 175 | // Success 176 | it(`should return a status code of "${HttpStatusCodes.OK}" if the ` + 177 | 'request was successful.', done => { 178 | spyOn(UserRepo, 'delete').and.resolveTo(); 179 | spyOn(UserRepo, 'persists').and.resolveTo(true); 180 | callApi(5, res => { 181 | expect(res.status).toBe(HttpStatusCodes.OK); 182 | done(); 183 | }); 184 | }); 185 | 186 | // User not found 187 | it('should return a JSON object with the error message of ' + 188 | `"${USER_NOT_FOUND_ERR}" and a status code of ` + 189 | `"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, done => { 190 | callApi(-1, res => { 191 | expect(res.status).toBe(HttpStatusCodes.NOT_FOUND); 192 | expect(res.body.error).toBe(USER_NOT_FOUND_ERR); 193 | done(); 194 | }); 195 | }); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /src/common/HttpStatusCodes.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /** 3 | * This file was copied from here: https://gist.github.com/scokmen/f813c904ef79022e84ab2409574d1b45 4 | */ 5 | 6 | /** 7 | * Hypertext Transfer Protocol (HTTP) response status codes. 8 | * @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes} 9 | */ 10 | enum HttpStatusCodes { 11 | 12 | /** 13 | * The server has received the request headers and the client should proceed to send the request body 14 | * (in the case of a request for which a body needs to be sent; for example, a POST request). 15 | * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. 16 | * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request 17 | * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. 18 | */ 19 | CONTINUE = 100, 20 | 21 | /** 22 | * The requester has asked the server to switch protocols and the server has agreed to do so. 23 | */ 24 | SWITCHING_PROTOCOLS = 101, 25 | 26 | /** 27 | * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. 28 | * This code indicates that the server has received and is processing the request, but no response is available yet. 29 | * This prevents the client from timing out and assuming the request was lost. 30 | */ 31 | PROCESSING = 102, 32 | 33 | /** 34 | * Standard response for successful HTTP requests. 35 | * The actual response will depend on the request method used. 36 | * In a GET request, the response will contain an entity corresponding to the requested resource. 37 | * In a POST request, the response will contain an entity describing or containing the result of the action. 38 | */ 39 | OK = 200, 40 | 41 | /** 42 | * The request has been fulfilled, resulting in the creation of a new resource. 43 | */ 44 | CREATED = 201, 45 | 46 | /** 47 | * The request has been accepted for processing, but the processing has not been completed. 48 | * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. 49 | */ 50 | ACCEPTED = 202, 51 | 52 | /** 53 | * SINCE HTTP/1.1 54 | * The server is a transforming proxy that received a 200 OK from its origin, 55 | * but is returning a modified version of the origin's response. 56 | */ 57 | NON_AUTHORITATIVE_INFORMATION = 203, 58 | 59 | /** 60 | * The server successfully processed the request and is not returning any content. 61 | */ 62 | NO_CONTENT = 204, 63 | 64 | /** 65 | * The server successfully processed the request, but is not returning any content. 66 | * Unlike a 204 response, this response requires that the requester reset the document view. 67 | */ 68 | RESET_CONTENT = 205, 69 | 70 | /** 71 | * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. 72 | * The range header is used by HTTP clients to enable resuming of interrupted downloads, 73 | * or split a download into multiple simultaneous streams. 74 | */ 75 | PARTIAL_CONTENT = 206, 76 | 77 | /** 78 | * The message body that follows is an XML message and can contain a number of separate response codes, 79 | * depending on how many sub-requests were made. 80 | */ 81 | MULTI_STATUS = 207, 82 | 83 | /** 84 | * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, 85 | * and are not being included again. 86 | */ 87 | ALREADY_REPORTED = 208, 88 | 89 | /** 90 | * The server has fulfilled a request for the resource, 91 | * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. 92 | */ 93 | IM_USED = 226, 94 | 95 | /** 96 | * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). 97 | * For example, this code could be used to present multiple video format options, 98 | * to list files with different filename extensions, or to suggest word-sense disambiguation. 99 | */ 100 | MULTIPLE_CHOICES = 300, 101 | 102 | /** 103 | * This and all future requests should be directed to the given URI. 104 | */ 105 | MOVED_PERMANENTLY = 301, 106 | 107 | /** 108 | * This is an example of industry practice contradicting the standard. 109 | * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect 110 | * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 111 | * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 112 | * to distinguish between the two behaviours. However, some Web applications and frameworks 113 | * use the 302 status code as if it were the 303. 114 | */ 115 | FOUND = 302, 116 | 117 | /** 118 | * SINCE HTTP/1.1 119 | * The response to the request can be found under another URI using a GET method. 120 | * When received in response to a POST (or PUT/DELETE), the client should presume that 121 | * the server has received the data and should issue a redirect with a separate GET message. 122 | */ 123 | SEE_OTHER = 303, 124 | 125 | /** 126 | * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. 127 | * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. 128 | */ 129 | NOT_MODIFIED = 304, 130 | 131 | /** 132 | * SINCE HTTP/1.1 133 | * The requested resource is available only through a proxy, the address for which is provided in the response. 134 | * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. 135 | */ 136 | USE_PROXY = 305, 137 | 138 | /** 139 | * No longer used. Originally meant "Subsequent requests should use the specified proxy." 140 | */ 141 | SWITCH_PROXY = 306, 142 | 143 | /** 144 | * SINCE HTTP/1.1 145 | * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. 146 | * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. 147 | * For example, a POST request should be repeated using another POST request. 148 | */ 149 | TEMPORARY_REDIRECT = 307, 150 | 151 | /** 152 | * The request and all future requests should be repeated using another URI. 153 | * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. 154 | * So, for example, submitting a form to a permanently redirected resource may continue smoothly. 155 | */ 156 | PERMANENT_REDIRECT = 308, 157 | 158 | /** 159 | * The server cannot or will not process the request due to an apparent client error 160 | * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). 161 | */ 162 | BAD_REQUEST = 400, 163 | 164 | /** 165 | * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet 166 | * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the 167 | * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means 168 | * "unauthenticated",i.e. the user does not have the necessary credentials. 169 | */ 170 | UNAUTHORIZED = 401, 171 | 172 | /** 173 | * Reserved for future use. The original intention was that this code might be used as part of some form of digital 174 | * cash or micro payment scheme, but that has not happened, and this code is not usually used. 175 | * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. 176 | */ 177 | PAYMENT_REQUIRED = 402, 178 | 179 | /** 180 | * The request was valid, but the server is refusing action. 181 | * The user might not have the necessary permissions for a resource. 182 | */ 183 | FORBIDDEN = 403, 184 | 185 | /** 186 | * The requested resource could not be found but may be available in the future. 187 | * Subsequent requests by the client are permissible. 188 | */ 189 | NOT_FOUND = 404, 190 | 191 | /** 192 | * A request method is not supported for the requested resource; 193 | * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. 194 | */ 195 | METHOD_NOT_ALLOWED = 405, 196 | 197 | /** 198 | * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. 199 | */ 200 | NOT_ACCEPTABLE = 406, 201 | 202 | /** 203 | * The client must first authenticate itself with the proxy. 204 | */ 205 | PROXY_AUTHENTICATION_REQUIRED = 407, 206 | 207 | /** 208 | * The server timed out waiting for the request. 209 | * According to HTTP specifications: 210 | * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." 211 | */ 212 | REQUEST_TIMEOUT = 408, 213 | 214 | /** 215 | * Indicates that the request could not be processed because of conflict in the request, 216 | * such as an edit conflict between multiple simultaneous updates. 217 | */ 218 | CONFLICT = 409, 219 | 220 | /** 221 | * Indicates that the resource requested is no longer available and will not be available again. 222 | * This should be used when a resource has been intentionally removed and the resource should be purged. 223 | * Upon receiving a 410 status code, the client should not request the resource in the future. 224 | * Clients such as search engines should remove the resource from their indices. 225 | * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. 226 | */ 227 | GONE = 410, 228 | 229 | /** 230 | * The request did not specify the length of its content, which is required by the requested resource. 231 | */ 232 | LENGTH_REQUIRED = 411, 233 | 234 | /** 235 | * The server does not meet one of the preconditions that the requester put on the request. 236 | */ 237 | PRECONDITION_FAILED = 412, 238 | 239 | /** 240 | * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". 241 | */ 242 | PAYLOAD_TOO_LARGE = 413, 243 | 244 | /** 245 | * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, 246 | * in which case it should be converted to a POST request. 247 | * Called "Request-URI Too Long" previously. 248 | */ 249 | URI_TOO_LONG = 414, 250 | 251 | /** 252 | * The request entity has a media type which the server or resource does not support. 253 | * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. 254 | */ 255 | UNSUPPORTED_MEDIA_TYPE = 415, 256 | 257 | /** 258 | * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. 259 | * For example, if the client asked for a part of the file that lies beyond the end of the file. 260 | * Called "Requested Range Not Satisfiable" previously. 261 | */ 262 | RANGE_NOT_SATISFIABLE = 416, 263 | 264 | /** 265 | * The server cannot meet the requirements of the Expect request-header field. 266 | */ 267 | EXPECTATION_FAILED = 417, 268 | 269 | /** 270 | * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, 271 | * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by 272 | * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. 273 | */ 274 | I_AM_A_TEAPOT = 418, 275 | 276 | /** 277 | * The request was directed at a server that is not able to produce a response (for example because a connection reuse). 278 | */ 279 | MISDIRECTED_REQUEST = 421, 280 | 281 | /** 282 | * The request was well-formed but was unable to be followed due to semantic errors. 283 | */ 284 | UNPROCESSABLE_ENTITY = 422, 285 | 286 | /** 287 | * The resource that is being accessed is locked. 288 | */ 289 | LOCKED = 423, 290 | 291 | /** 292 | * The request failed due to failure of a previous request (e.g., a PROPPATCH). 293 | */ 294 | FAILED_DEPENDENCY = 424, 295 | 296 | /** 297 | * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. 298 | */ 299 | UPGRADE_REQUIRED = 426, 300 | 301 | /** 302 | * The origin server requires the request to be conditional. 303 | * Intended to prevent "the 'lost update' problem, where a client 304 | * GETs a resource's state, modifies it, and PUTs it back to the server, 305 | * when meanwhile a third party has modified the state on the server, leading to a conflict." 306 | */ 307 | PRECONDITION_REQUIRED = 428, 308 | 309 | /** 310 | * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. 311 | */ 312 | TOO_MANY_REQUESTS = 429, 313 | 314 | /** 315 | * The server is unwilling to process the request because either an individual header field, 316 | * or all the header fields collectively, are too large. 317 | */ 318 | REQUEST_HEADER_FIELDS_TOO_LARGE = 431, 319 | 320 | /** 321 | * A server operator has received a legal demand to deny access to a resource or to a set of resources 322 | * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. 323 | */ 324 | UNAVAILABLE_FOR_LEGAL_REASONS = 451, 325 | 326 | /** 327 | * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. 328 | */ 329 | INTERNAL_SERVER_ERROR = 500, 330 | 331 | /** 332 | * The server either does not recognize the request method, or it lacks the ability to fulfill the request. 333 | * Usually this implies future availability (e.g., a new feature of a web-service API). 334 | */ 335 | NOT_IMPLEMENTED = 501, 336 | 337 | /** 338 | * The server was acting as a gateway or proxy and received an invalid response from the upstream server. 339 | */ 340 | BAD_GATEWAY = 502, 341 | 342 | /** 343 | * The server is currently unavailable (because it is overloaded or down for maintenance). 344 | * Generally, this is a temporary state. 345 | */ 346 | SERVICE_UNAVAILABLE = 503, 347 | 348 | /** 349 | * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. 350 | */ 351 | GATEWAY_TIMEOUT = 504, 352 | 353 | /** 354 | * The server does not support the HTTP protocol version used in the request 355 | */ 356 | HTTP_VERSION_NOT_SUPPORTED = 505, 357 | 358 | /** 359 | * Transparent content negotiation for the request results in a circular reference. 360 | */ 361 | VARIANT_ALSO_NEGOTIATES = 506, 362 | 363 | /** 364 | * The server is unable to store the representation needed to complete the request. 365 | */ 366 | INSUFFICIENT_STORAGE = 507, 367 | 368 | /** 369 | * The server detected an infinite loop while processing the request. 370 | */ 371 | LOOP_DETECTED = 508, 372 | 373 | /** 374 | * Further extensions to the request are required for the server to fulfill it. 375 | */ 376 | NOT_EXTENDED = 510, 377 | 378 | /** 379 | * The client needs to authenticate to gain network access. 380 | * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used 381 | * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). 382 | */ 383 | NETWORK_AUTHENTICATION_REQUIRED = 511 384 | } 385 | 386 | export default HttpStatusCodes; 387 | -------------------------------------------------------------------------------- /src/public/lib/bootstrap/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v5.3.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>n(t))).join(","):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="next",lt="prev",ct="left",ht="right",dt=`slide${ot}`,ut=`slid${ot}`,ft=`keydown${ot}`,pt=`mouseenter${ot}`,mt=`mouseleave${ot}`,gt=`dragstart${ot}`,_t=`load${ot}${rt}`,bt=`click${ot}${rt}`,vt="carousel",yt="active",wt=".active",At=".carousel-item",Et=wt+At,Tt={ArrowLeft:ht,ArrowRight:ct},Ct={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ot={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class xt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===vt&&this.cycle()}static get Default(){return Ct}static get DefaultType(){return Ot}static get NAME(){return"carousel"}next(){this._slide(at)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(lt)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,ut,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,ut,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?at:lt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,ft,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,pt,(()=>this.pause())),N.on(this._element,mt,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,gt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ct)),rightCallback:()=>this._slide(this._directionToOrder(ht)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Tt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(wt,this._indicatorsElement);e.classList.remove(yt),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(yt),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===at,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(dt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(yt),i.classList.remove(yt,c,l),this._isSliding=!1,r(ut)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Et,this._element)}_getItems(){return z.find(At,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ct?lt:at:t===ct?at:lt}_orderToDirection(t){return p()?t===lt?ct:ht:t===lt?ht:ct}static jQueryInterface(t){return this.each((function(){const e=xt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,bt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(vt))return;t.preventDefault();const i=xt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,_t,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)xt.getOrCreateInstance(e)})),m(xt);const kt=".bs.collapse",Lt=`show${kt}`,St=`shown${kt}`,Dt=`hide${kt}`,$t=`hidden${kt}`,It=`click${kt}.data-api`,Nt="show",Pt="collapse",jt="collapsing",Mt=`:scope .${Pt} .${Pt}`,Ft='[data-bs-toggle="collapse"]',Ht={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Bt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Ft);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Ht}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Bt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Lt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Pt),this._element.classList.add(jt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt,Nt),this._element.style[e]="",N.trigger(this._element,St)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,Dt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(jt),this._element.classList.remove(Pt,Nt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(jt),this._element.classList.add(Pt),N.trigger(this._element,$t)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(Nt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Ft);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(Mt,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Bt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,It,Ft,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Bt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Bt);var zt="top",Rt="bottom",qt="right",Vt="left",Kt="auto",Qt=[zt,Rt,qt,Vt],Xt="start",Yt="end",Ut="clippingParents",Gt="viewport",Jt="popper",Zt="reference",te=Qt.reduce((function(t,e){return t.concat([e+"-"+Xt,e+"-"+Yt])}),[]),ee=[].concat(Qt,[Kt]).reduce((function(t,e){return t.concat([e,e+"-"+Xt,e+"-"+Yt])}),[]),ie="beforeRead",ne="read",se="afterRead",oe="beforeMain",re="main",ae="afterMain",le="beforeWrite",ce="write",he="afterWrite",de=[ie,ne,se,oe,re,ae,le,ce,he];function ue(t){return t?(t.nodeName||"").toLowerCase():null}function fe(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function pe(t){return t instanceof fe(t).Element||t instanceof Element}function me(t){return t instanceof fe(t).HTMLElement||t instanceof HTMLElement}function ge(t){return"undefined"!=typeof ShadowRoot&&(t instanceof fe(t).ShadowRoot||t instanceof ShadowRoot)}const _e={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];me(s)&&ue(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});me(n)&&ue(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function be(t){return t.split("-")[0]}var ve=Math.max,ye=Math.min,we=Math.round;function Ae(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ee(){return!/^((?!chrome|android).)*safari/i.test(Ae())}function Te(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&me(t)&&(s=t.offsetWidth>0&&we(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&we(n.height)/t.offsetHeight||1);var r=(pe(t)?fe(t):window).visualViewport,a=!Ee()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function Ce(t){var e=Te(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Oe(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ge(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function xe(t){return fe(t).getComputedStyle(t)}function ke(t){return["table","td","th"].indexOf(ue(t))>=0}function Le(t){return((pe(t)?t.ownerDocument:t.document)||window.document).documentElement}function Se(t){return"html"===ue(t)?t:t.assignedSlot||t.parentNode||(ge(t)?t.host:null)||Le(t)}function De(t){return me(t)&&"fixed"!==xe(t).position?t.offsetParent:null}function $e(t){for(var e=fe(t),i=De(t);i&&ke(i)&&"static"===xe(i).position;)i=De(i);return i&&("html"===ue(i)||"body"===ue(i)&&"static"===xe(i).position)?e:i||function(t){var e=/firefox/i.test(Ae());if(/Trident/i.test(Ae())&&me(t)&&"fixed"===xe(t).position)return null;var i=Se(t);for(ge(i)&&(i=i.host);me(i)&&["html","body"].indexOf(ue(i))<0;){var n=xe(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Ie(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Ne(t,e,i){return ve(t,ye(e,i))}function Pe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function je(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const Me={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=be(i.placement),l=Ie(a),c=[Vt,qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Pe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:je(t,Qt))}(s.padding,i),d=Ce(o),u="y"===l?zt:Vt,f="y"===l?Rt:qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=$e(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Ne(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Oe(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Fe(t){return t.split("-")[1]}var He={top:"auto",right:"auto",bottom:"auto",left:"auto"};function We(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Vt,y=zt,w=window;if(c){var A=$e(i),E="clientHeight",T="clientWidth";A===fe(i)&&"static"!==xe(A=Le(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===zt||(s===Vt||s===qt)&&o===Yt)&&(y=Rt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Vt&&(s!==zt&&s!==Rt||o!==Yt)||(v=qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&He),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:we(i*s)/s||0,y:we(n*s)/s||0}}({x:f,y:m},fe(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Be={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:be(e.placement),variation:Fe(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,We(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,We(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var ze={passive:!0};const Re={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=fe(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,ze)})),a&&l.addEventListener("resize",i.update,ze),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,ze)})),a&&l.removeEventListener("resize",i.update,ze)}},data:{}};var qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Ve(t){return t.replace(/left|right|bottom|top/g,(function(t){return qe[t]}))}var Ke={start:"end",end:"start"};function Qe(t){return t.replace(/start|end/g,(function(t){return Ke[t]}))}function Xe(t){var e=fe(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ye(t){return Te(Le(t)).left+Xe(t).scrollLeft}function Ue(t){var e=xe(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ge(t){return["html","body","#document"].indexOf(ue(t))>=0?t.ownerDocument.body:me(t)&&Ue(t)?t:Ge(Se(t))}function Je(t,e){var i;void 0===e&&(e=[]);var n=Ge(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=fe(n),r=s?[o].concat(o.visualViewport||[],Ue(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Je(Se(r)))}function Ze(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ti(t,e,i){return e===Gt?Ze(function(t,e){var i=fe(t),n=Le(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ee();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ye(t),y:l}}(t,i)):pe(e)?function(t,e){var i=Te(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):Ze(function(t){var e,i=Le(t),n=Xe(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=ve(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=ve(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ye(t),l=-n.scrollTop;return"rtl"===xe(s||i).direction&&(a+=ve(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(Le(t)))}function ei(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?be(s):null,r=s?Fe(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case zt:e={x:a,y:i.y-n.height};break;case Rt:e={x:a,y:i.y+i.height};break;case qt:e={x:i.x+i.width,y:l};break;case Vt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Ie(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Xt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Yt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function ii(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Ut:a,c=i.rootBoundary,h=void 0===c?Gt:c,d=i.elementContext,u=void 0===d?Jt:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Pe("number"!=typeof g?g:je(g,Qt)),b=u===Jt?Zt:Jt,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=Je(Se(t)),i=["absolute","fixed"].indexOf(xe(t).position)>=0&&me(t)?$e(t):t;return pe(i)?e.filter((function(t){return pe(t)&&Oe(t,i)&&"body"!==ue(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ti(t,i,n);return e.top=ve(s.top,e.top),e.right=ye(s.right,e.right),e.bottom=ye(s.bottom,e.bottom),e.left=ve(s.left,e.left),e}),ti(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(pe(y)?y:y.contextElement||Le(t.elements.popper),l,h,r),A=Te(t.elements.reference),E=ei({reference:A,element:v,strategy:"absolute",placement:s}),T=Ze(Object.assign({},v,E)),C=u===Jt?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===Jt&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[qt,Rt].indexOf(t)>=0?1:-1,i=[zt,Rt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function ni(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ee:l,h=Fe(n),d=h?a?te:te.filter((function(t){return Fe(t)===h})):Qt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=ii(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[be(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const si={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=be(g),b=l||(_!==g&&p?function(t){if(be(t)===Kt)return[];var e=Ve(t);return[Qe(t),e,Qe(e)]}(g):[Ve(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(be(i)===Kt?ni(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=ii(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?qt:Vt:k?Rt:zt;y[S]>w[S]&&($=Ve($));var I=Ve($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function oi(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ri(t){return[zt,qt,Rt,Vt].some((function(e){return t[e]>=0}))}const ai={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=ii(e,{elementContext:"reference"}),a=ii(e,{altBoundary:!0}),l=oi(r,n),c=oi(a,s,o),h=ri(l),d=ri(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},li={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ee.reduce((function(t,i){return t[i]=function(t,e,i){var n=be(t),s=[Vt,zt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Vt,qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ci={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ei({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},hi={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=ii(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=be(e.placement),b=Fe(e.placement),v=!b,y=Ie(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?zt:Vt,D="y"===y?Rt:qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Xt?E[$]:T[$],F=b===Xt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?Ce(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Ne(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&$e(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Ne(f?ye(N,I+V-Y-X):N,I,f?ve(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?zt:Vt,tt="x"===y?Rt:qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[zt,Vt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Ne(t,e,i);return n>i?i:n}(at,et,lt):Ne(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function di(t,e,i){void 0===i&&(i=!1);var n,s,o=me(e),r=me(e)&&function(t){var e=t.getBoundingClientRect(),i=we(e.width)/t.offsetWidth||1,n=we(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=Le(e),l=Te(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ue(e)||Ue(a))&&(c=(n=e)!==fe(n)&&me(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Xe(n)),me(e)?((h=Te(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ye(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function ui(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var fi={placement:"bottom",modifiers:[],strategy:"absolute"};function pi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ei,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:z.prev(this,Ii)[0]||z.next(this,Ii)[0]||z.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,Si,Ii,qi.dataApiKeydownHandler),N.on(document,Si,Pi,qi.dataApiKeydownHandler),N.on(document,Li,qi.clearMenus),N.on(document,Di,qi.clearMenus),N.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),m(qi);const Vi="backdrop",Ki="show",Qi=`mousedown.bs.${Vi}`,Xi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Yi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Xi}static get DefaultType(){return Yi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Ki),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Ki),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Qi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Qi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Gi),N.on(document,Ji,(t=>this._handleFocusin(t))),N.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",An="show",En="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(An),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,hn),N.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(An),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,bn,(t=>{N.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(En)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(En),this._queueCallback((()=>{this._element.classList.remove(En),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,pn,(t=>{t.defaultPrevented||N.one(e,fn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),R(On),m(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,jn=`hide${xn}`,Mn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Wn=`click${xn}${kn}`,Bn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),N.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,jn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),N.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,Mn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Bn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Mn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,Wn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Fn,(()=>{a(this)&&this.focus()}));const i=z.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),N.on(window,Ln,(()=>{for(const t of z.find(In))qn.getOrCreateInstance(t).show()})),N.on(window,Hn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),R(qn),m(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Kn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Qn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Xn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Kn.has(i)||Boolean(Qn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Yn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Yn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Xn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends W{constructor(t,e){if(void 0===vi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return bi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},As={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Es extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return As}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ms),N.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(bs,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),N.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=z.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=Es.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,gs,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))Es.getOrCreateInstance(t)})),m(Es);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",js="Home",Ms="End",Fs="active",Hs="fade",Ws="show",Bs=".dropdown-toggle",zs=`:not(${Bs})`,Rs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',qs=`.nav-link${zs}, .list-group-item${zs}, [role="tab"]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Ks extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Cs,{relatedTarget:t}):null;N.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,ks,{relatedTarget:e})):t.classList.add(Ws)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Ws)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,js,Ms].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([js,Ms].includes(t.key))i=e[t.key===js?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ks.getOrCreateInstance(i).show())}_getChildren(){return z.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Bs,Fs),n(".dropdown-menu",Ws),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(qs)?t:z.findOne(qs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Ks.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ls,Rs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Ks.getOrCreateInstance(this).show()})),N.on(window,Ds,(()=>{for(const t of z.find(Vs))Ks.getOrCreateInstance(t)})),m(Ks);const Qs=".bs.toast",Xs=`mouseover${Qs}`,Ys=`mouseout${Qs}`,Us=`focusin${Qs}`,Gs=`focusout${Qs}`,Js=`hide${Qs}`,Zs=`hidden${Qs}`,to=`show${Qs}`,eo=`shown${Qs}`,io="hide",no="show",so="showing",oo={animation:"boolean",autohide:"boolean",delay:"number"},ro={animation:!0,autohide:!0,delay:5e3};class ao extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return ro}static get DefaultType(){return oo}static get NAME(){return"toast"}show(){N.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(io),d(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),N.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),N.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Xs,(t=>this._onInteraction(t,!0))),N.on(this._element,Ys,(t=>this._onInteraction(t,!1))),N.on(this._element,Us,(t=>this._onInteraction(t,!0))),N.on(this._element,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(ao),m(ao),{Alert:Q,Button:Y,Carousel:xt,Collapse:Bt,Dropdown:qi,Modal:On,Offcanvas:qn,Popover:us,ScrollSpy:Es,Tab:Ks,Toast:ao,Tooltip:cs}})); -------------------------------------------------------------------------------- /src/public/lib/handlebars/handlebars.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?exports.Handlebars=b():a.Handlebars=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){"use strict";function d(){var a=r();return a.compile=function(b,c){return k.compile(b,c,a)},a.precompile=function(b,c){return k.precompile(b,c,a)},a.AST=i["default"],a.Compiler=k.Compiler,a.JavaScriptCompiler=m["default"],a.Parser=j.parser,a.parse=j.parse,a.parseWithoutProcessing=j.parseWithoutProcessing,a}var e=c(1)["default"];b.__esModule=!0;var f=c(2),g=e(f),h=c(84),i=e(h),j=c(85),k=c(90),l=c(91),m=e(l),n=c(88),o=e(n),p=c(83),q=e(p),r=g["default"].create,s=d();s.create=d,q["default"](s),s.Visitor=o["default"],s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){return a&&a.__esModule?a:{"default":a}},b.__esModule=!0},function(a,b,c){"use strict";function d(){var a=new h.HandlebarsEnvironment;return n.extend(a,h),a.SafeString=j["default"],a.Exception=l["default"],a.Utils=n,a.escapeExpression=n.escapeExpression,a.VM=p,a.template=function(b){return p.template(b,a)},a}var e=c(3)["default"],f=c(1)["default"];b.__esModule=!0;var g=c(4),h=e(g),i=c(77),j=f(i),k=c(6),l=f(k),m=c(5),n=e(m),o=c(78),p=e(o),q=c(83),r=f(q),s=d();s.create=d,r["default"](s),s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b},b.__esModule=!0},function(a,b,c){"use strict";function d(a,b,c){this.helpers=a||{},this.partials=b||{},this.decorators=c||{},i.registerDefaultHelpers(this),j.registerDefaultDecorators(this)}var e=c(1)["default"];b.__esModule=!0,b.HandlebarsEnvironment=d;var f=c(5),g=c(6),h=e(g),i=c(10),j=c(70),k=c(72),l=e(k),m=c(73),n="4.7.8";b.VERSION=n;var o=8;b.COMPILER_REVISION=o;var p=7;b.LAST_COMPATIBLE_COMPILER_REVISION=p;var q={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};b.REVISION_CHANGES=q;var r="[object Object]";d.prototype={constructor:d,logger:l["default"],log:l["default"].log,registerHelper:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){if(f.toString.call(a)===r)f.extend(this.partials,a);else{if("undefined"==typeof b)throw new h["default"]('Attempting to register a partial called "'+a+'" as undefined');this.partials[a]=b}},unregisterPartial:function(a){delete this.partials[a]},registerDecorator:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple decorators");f.extend(this.decorators,a)}else this.decorators[a]=b},unregisterDecorator:function(a){delete this.decorators[a]},resetLoggedPropertyAccesses:function(){m.resetLoggedProperties()}};var s=l["default"].log;b.log=s,b.createFrame=f.createFrame,b.logger=l["default"]},function(a,b){"use strict";function c(a){return k[a]}function d(a){for(var b=1;b":">",'"':""","'":"'","`":"`","=":"="},l=/[&<>"'`=]/g,m=/[&<>"'`=]/,n=Object.prototype.toString;b.toString=n;var o=function(a){return"function"==typeof a};o(/x/)&&(b.isFunction=o=function(a){return"function"==typeof a&&"[object Function]"===n.call(a)}),b.isFunction=o;var p=Array.isArray||function(a){return!(!a||"object"!=typeof a)&&"[object Array]"===n.call(a)};b.isArray=p},function(a,b,c){"use strict";function d(a,b){var c=b&&b.loc,g=void 0,h=void 0,i=void 0,j=void 0;c&&(g=c.start.line,h=c.end.line,i=c.start.column,j=c.end.column,a+=" - "+g+":"+i);for(var k=Error.prototype.constructor.call(this,a),l=0;l0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):e(this);if(c.data&&c.ids){var g=d.createFrame(c.data);g.contextPath=d.appendContextPath(c.data.contextPath,c.name),c={data:g}}return f(b,c)})},a.exports=b["default"]},function(a,b,c){"use strict";var d=c(13)["default"],e=c(43)["default"],f=c(55)["default"],g=c(60)["default"],h=c(1)["default"];b.__esModule=!0;var i=c(5),j=c(6),k=h(j);b["default"]=function(a){a.registerHelper("each",function(a,b){function c(b,c,d){n&&(n.key=b,n.index=c,n.first=0===c,n.last=!!d,o&&(n.contextPath=o+b)),m+=h(a[b],{data:n,blockParams:i.blockParams([a[b],b],[o+b,null])})}if(!b)throw new k["default"]("Must pass iterator to #each");var h=b.fn,j=b.inverse,l=0,m="",n=void 0,o=void 0;if(b.data&&b.ids&&(o=i.appendContextPath(b.data.contextPath,b.ids[0])+"."),i.isFunction(a)&&(a=a.call(this)),b.data&&(n=i.createFrame(b.data)),a&&"object"==typeof a)if(i.isArray(a))for(var p=a.length;le;)M(a,c=d[e++],b[c]);return a},O=function(a,b){return void 0===b?x(a):N(x(a),b)},P=function(a){var b=E.call(this,a);return!(b||!f(this,a)||!f(G,a)||f(this,D)&&this[D][a])||b},Q=function(a,b){var c=v(a=t(a),b);return!c||!f(G,b)||f(a,D)&&a[D][b]||(c.enumerable=!0),c},R=function(a){for(var b,c=y(t(a)),d=[],e=0;c.length>e;)f(G,b=c[e++])||b==D||d.push(b);return d},S=function(a){for(var b,c=y(t(a)),d=[],e=0;c.length>e;)f(G,b=c[e++])&&d.push(G[b]);return d},T=function(a){if(void 0!==a&&!L(a)){for(var b,c,d=[a],e=1,f=arguments;f.length>e;)d.push(f[e++]);return b=d[1],"function"==typeof b&&(c=b),!c&&r(b)||(b=function(a,b){if(c&&(b=c.call(this,a,b)),!L(b))return b}),d[1]=b,B.apply(A,d)}},U=j(function(){var a=z();return"[null]"!=B([a])||"{}"!=B({a:a})||"{}"!=B(Object(a))});H||(z=function(){if(L(this))throw TypeError("Symbol is not a constructor");return K(m(arguments.length>0?arguments[0]:void 0))},i(z.prototype,"toString",function(){return this._k}),L=function(a){return a instanceof z},d.create=O,d.isEnum=P,d.getDesc=Q,d.setDesc=M,d.setDescs=N,d.getNames=p.get=R,d.getSymbols=S,g&&!c(41)&&i(I,"propertyIsEnumerable",P,!0));var V={"for":function(a){return f(F,a+="")?F[a]:F[a]=z(a)},keyFor:function(a){return o(F,a)},useSetter:function(){C=!0},useSimple:function(){C=!1}};d.each.call("hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),function(a){var b=n(a);V[a]=H?b:K(b)}),C=!0,h(h.G+h.W,{Symbol:z}),h(h.S,"Symbol",V),h(h.S+h.F*!H,"Object",{create:O,defineProperty:M,defineProperties:N,getOwnPropertyDescriptor:Q,getOwnPropertyNames:R,getOwnPropertySymbols:S}),A&&h(h.S+h.F*(!H||U),"JSON",{stringify:T}),l(z,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(a,b){var c=a.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=c)},function(a,b){var c={}.hasOwnProperty;a.exports=function(a,b){return c.call(a,b)}},function(a,b,c){a.exports=!c(19)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(a,b){a.exports=function(a){try{return!!a()}catch(b){return!0}}},function(a,b,c){var d=c(16),e=c(21),f=c(22),g="prototype",h=function(a,b,c){var i,j,k,l=a&h.F,m=a&h.G,n=a&h.S,o=a&h.P,p=a&h.B,q=a&h.W,r=m?e:e[b]||(e[b]={}),s=m?d:n?d[b]:(d[b]||{})[g];m&&(c=b);for(i in c)j=!l&&s&&i in s,j&&i in r||(k=j?s[i]:c[i],r[i]=m&&"function"!=typeof s[i]?c[i]:p&&j?f(k,d):q&&s[i]==k?function(a){var b=function(b){return this instanceof a?new a(b):a(b)};return b[g]=a[g],b}(k):o&&"function"==typeof k?f(Function.call,k):k,o&&((r[g]||(r[g]={}))[i]=k))};h.F=1,h.G=2,h.S=4,h.P=8,h.B=16,h.W=32,a.exports=h},function(a,b){var c=a.exports={version:"1.2.6"};"number"==typeof __e&&(__e=c)},function(a,b,c){var d=c(23);a.exports=function(a,b,c){if(d(a),void 0===b)return a;switch(c){case 1:return function(c){return a.call(b,c)};case 2:return function(c,d){return a.call(b,c,d)};case 3:return function(c,d,e){return a.call(b,c,d,e)}}return function(){return a.apply(b,arguments)}}},function(a,b){a.exports=function(a){if("function"!=typeof a)throw TypeError(a+" is not a function!");return a}},function(a,b,c){a.exports=c(25)},function(a,b,c){var d=c(9),e=c(26);a.exports=c(18)?function(a,b,c){return d.setDesc(a,b,e(1,c))}:function(a,b,c){return a[b]=c,a}},function(a,b){a.exports=function(a,b){return{enumerable:!(1&a),configurable:!(2&a),writable:!(4&a),value:b}}},function(a,b,c){var d=c(16),e="__core-js_shared__",f=d[e]||(d[e]={});a.exports=function(a){return f[a]||(f[a]={})}},function(a,b,c){var d=c(9).setDesc,e=c(17),f=c(29)("toStringTag");a.exports=function(a,b,c){a&&!e(a=c?a:a.prototype,f)&&d(a,f,{configurable:!0,value:b})}},function(a,b,c){var d=c(27)("wks"),e=c(30),f=c(16).Symbol;a.exports=function(a){return d[a]||(d[a]=f&&f[a]||(f||e)("Symbol."+a))}},function(a,b){var c=0,d=Math.random();a.exports=function(a){return"Symbol(".concat(void 0===a?"":a,")_",(++c+d).toString(36))}},function(a,b,c){var d=c(9),e=c(32);a.exports=function(a,b){for(var c,f=e(a),g=d.getKeys(f),h=g.length,i=0;h>i;)if(f[c=g[i++]]===b)return c}},function(a,b,c){var d=c(33),e=c(35);a.exports=function(a){return d(e(a))}},function(a,b,c){var d=c(34);a.exports=Object("z").propertyIsEnumerable(0)?Object:function(a){return"String"==d(a)?a.split(""):Object(a)}},function(a,b){var c={}.toString;a.exports=function(a){return c.call(a).slice(8,-1)}},function(a,b){a.exports=function(a){if(void 0==a)throw TypeError("Can't call method on "+a);return a}},function(a,b,c){var d=c(32),e=c(9).getNames,f={}.toString,g="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],h=function(a){try{return e(a)}catch(b){return g.slice()}};a.exports.get=function(a){return g&&"[object Window]"==f.call(a)?h(a):e(d(a))}},function(a,b,c){var d=c(9);a.exports=function(a){var b=d.getKeys(a),c=d.getSymbols;if(c)for(var e,f=c(a),g=d.isEnum,h=0;f.length>h;)g.call(a,e=f[h++])&&b.push(e);return b}},function(a,b,c){var d=c(34);a.exports=Array.isArray||function(a){return"Array"==d(a)}},function(a,b,c){var d=c(40);a.exports=function(a){if(!d(a))throw TypeError(a+" is not an object!");return a}},function(a,b){a.exports=function(a){return"object"==typeof a?null!==a:"function"==typeof a}},function(a,b){a.exports=!0},function(a,b){},function(a,b,c){a.exports={"default":c(44),__esModule:!0}},function(a,b,c){c(45),c(51),a.exports=c(29)("iterator")},function(a,b,c){"use strict";var d=c(46)(!0);c(48)(String,"String",function(a){this._t=String(a),this._i=0},function(){var a,b=this._t,c=this._i;return c>=b.length?{value:void 0,done:!0}:(a=d(b,c),this._i+=a.length,{value:a,done:!1})})},function(a,b,c){var d=c(47),e=c(35);a.exports=function(a){return function(b,c){var f,g,h=String(e(b)),i=d(c),j=h.length;return i<0||i>=j?a?"":void 0:(f=h.charCodeAt(i),f<55296||f>56319||i+1===j||(g=h.charCodeAt(i+1))<56320||g>57343?a?h.charAt(i):f:a?h.slice(i,i+2):(f-55296<<10)+(g-56320)+65536)}}},function(a,b){var c=Math.ceil,d=Math.floor;a.exports=function(a){return isNaN(a=+a)?0:(a>0?d:c)(a)}},function(a,b,c){"use strict";var d=c(41),e=c(20),f=c(24),g=c(25),h=c(17),i=c(49),j=c(50),k=c(28),l=c(9).getProto,m=c(29)("iterator"),n=!([].keys&&"next"in[].keys()),o="@@iterator",p="keys",q="values",r=function(){return this};a.exports=function(a,b,c,s,t,u,v){j(c,b,s);var w,x,y=function(a){if(!n&&a in C)return C[a];switch(a){case p:return function(){return new c(this,a)};case q:return function(){return new c(this,a)}}return function(){return new c(this,a)}},z=b+" Iterator",A=t==q,B=!1,C=a.prototype,D=C[m]||C[o]||t&&C[t],E=D||y(t);if(D){var F=l(E.call(new a));k(F,z,!0),!d&&h(C,o)&&g(F,m,r),A&&D.name!==q&&(B=!0,E=function(){return D.call(this)})}if(d&&!v||!n&&!B&&C[m]||g(C,m,E),i[b]=E,i[z]=r,t)if(w={values:A?E:y(q),keys:u?E:y(p),entries:A?y("entries"):E},v)for(x in w)x in C||f(C,x,w[x]);else e(e.P+e.F*(n||B),b,w);return w}},function(a,b){a.exports={}},function(a,b,c){"use strict";var d=c(9),e=c(26),f=c(28),g={};c(25)(g,c(29)("iterator"),function(){return this}),a.exports=function(a,b,c){a.prototype=d.create(g,{next:e(1,c)}),f(a,b+" Iterator")}},function(a,b,c){c(52);var d=c(49);d.NodeList=d.HTMLCollection=d.Array},function(a,b,c){"use strict";var d=c(53),e=c(54),f=c(49),g=c(32);a.exports=c(48)(Array,"Array",function(a,b){this._t=g(a),this._i=0,this._k=b},function(){var a=this._t,b=this._k,c=this._i++;return!a||c>=a.length?(this._t=void 0,e(1)):"keys"==b?e(0,c):"values"==b?e(0,a[c]):e(0,[c,a[c]])},"values"),f.Arguments=f.Array,d("keys"),d("values"),d("entries")},function(a,b){a.exports=function(){}},function(a,b){a.exports=function(a,b){return{value:b,done:!!a}}},function(a,b,c){a.exports={"default":c(56),__esModule:!0}},function(a,b,c){c(51),c(45),a.exports=c(57)},function(a,b,c){var d=c(39),e=c(58);a.exports=c(21).getIterator=function(a){var b=e(a);if("function"!=typeof b)throw TypeError(a+" is not iterable!");return d(b.call(a))}},function(a,b,c){var d=c(59),e=c(29)("iterator"),f=c(49);a.exports=c(21).getIteratorMethod=function(a){if(void 0!=a)return a[e]||a["@@iterator"]||f[d(a)]}},function(a,b,c){var d=c(34),e=c(29)("toStringTag"),f="Arguments"==d(function(){return arguments}());a.exports=function(a){var b,c,g;return void 0===a?"Undefined":null===a?"Null":"string"==typeof(c=(b=Object(a))[e])?c:f?d(b):"Object"==(g=d(b))&&"function"==typeof b.callee?"Arguments":g}},function(a,b,c){a.exports={"default":c(61),__esModule:!0}},function(a,b,c){c(62),a.exports=c(21).Object.keys},function(a,b,c){var d=c(63);c(64)("keys",function(a){return function(b){return a(d(b))}})},function(a,b,c){var d=c(35);a.exports=function(a){return Object(d(a))}},function(a,b,c){var d=c(20),e=c(21),f=c(19);a.exports=function(a,b){var c=(e.Object||{})[a]||Object[a],g={};g[a]=b(c),d(d.S+d.F*f(function(){c(1)}),"Object",g)}},function(a,b,c){"use strict";var d=c(1)["default"];b.__esModule=!0;var e=c(6),f=d(e);b["default"]=function(a){a.registerHelper("helperMissing",function(){if(1!==arguments.length)throw new f["default"]('Missing helper: "'+arguments[arguments.length-1].name+'"')})},a.exports=b["default"]},function(a,b,c){"use strict";var d=c(1)["default"];b.__esModule=!0;var e=c(5),f=c(6),g=d(f);b["default"]=function(a){a.registerHelper("if",function(a,b){if(2!=arguments.length)throw new g["default"]("#if requires exactly one argument");return e.isFunction(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||e.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){if(2!=arguments.length)throw new g["default"]("#unless requires exactly one argument");return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})})},a.exports=b["default"]},function(a,b){"use strict";b.__esModule=!0,b["default"]=function(a){a.registerHelper("log",function(){for(var b=[void 0],c=arguments[arguments.length-1],d=0;d=0?b:parseInt(a,10)}return a},log:function(a){if(a=e.lookupLevel(a),"undefined"!=typeof console&&e.lookupLevel(e.level)<=a){var b=e.methodMap[a];console[b]||(b="log");for(var c=arguments.length,d=Array(c>1?c-1:0),f=1;f=v.LAST_COMPATIBLE_COMPILER_REVISION&&b<=v.COMPILER_REVISION)){if(b2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gb[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substring(a,b.yyleng-c+a)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(e(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:return this.popState(),44;case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(b.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:return 72;case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return b.yytext=e(1,2).replace(/\\"/g,'"'),80;case 32:return b.yytext=e(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:return 82;case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return b.yytext=b.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";function d(){var a=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"ContentStatement"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"ContentStatement"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"ContentStatement"===d.type&&(c||!d.rightStripped)){var e=d.value;d.value=d.value.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.value!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"ContentStatement"===d.type&&(c||!d.leftStripped)){var e=d.value;return d.value=d.value.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.value!==e,d.leftStripped}}var i=c(1)["default"];b.__esModule=!0;var j=c(88),k=i(j);d.prototype=new k["default"],d.prototype.Program=function(a){var b=!this.options.ignoreStandalone,c=!this.isRootSeen;this.isRootSeen=!0;for(var d=a.body,i=0,j=d.length;i0)throw new q["default"]("Invalid path: "+d,{loc:c});".."===i&&f++}}return{type:"PathExpression",data:a,depth:f,parts:e,original:d,loc:c}}function j(a,b,c,d,e,f){var g=d.charAt(3)||d.charAt(2),h="{"!==g&&"&"!==g,i=/\*/.test(d);return{type:i?"Decorator":"MustacheStatement",path:a,params:b,hash:c,escaped:h,strip:e,loc:this.locInfo(f)}}function k(a,b,c,e){d(a,c),e=this.locInfo(e);var f={type:"Program",body:b,strip:{},loc:e};return{type:"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:f,openStrip:{},inverseStrip:{},closeStrip:{},loc:e}}function l(a,b,c,e,f,g){e&&e.path&&d(a,e);var h=/\*/.test(a.open);b.blockParams=a.blockParams;var i=void 0,j=void 0;if(c){if(h)throw new q["default"]("Unexpected inverse block on decorator",c);c.chain&&(c.program.body[0].closeStrip=e.strip),j=c.strip,i=c.program}return f&&(f=i,i=b,b=f),{type:h?"DecoratorBlock":"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:b,inverse:i,openStrip:a.strip,inverseStrip:j,closeStrip:e&&e.strip,loc:this.locInfo(g)}}function m(a,b){if(!b&&a.length){var c=a[0].loc,d=a[a.length-1].loc;c&&d&&(b={source:c.source,start:{line:c.start.line,column:c.start.column},end:{line:d.end.line,column:d.end.column}})}return{type:"Program",body:a,strip:{},loc:b}}function n(a,b,c,e){return d(a,c),{type:"PartialBlockStatement",name:a.path,params:a.params,hash:a.hash,program:b,openStrip:a.strip,closeStrip:c&&c.strip,loc:this.locInfo(e)}}var o=c(1)["default"];b.__esModule=!0,b.SourceLocation=e,b.id=f,b.stripFlags=g,b.stripComment=h,b.preparePath=i,b.prepareMustache=j,b.prepareRawBlock=k,b.prepareBlock=l,b.prepareProgram=m,b.preparePartialBlock=n;var p=c(6),q=o(p)},function(a,b,c){"use strict";function d(){}function e(a,b,c){if(null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a,b),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function f(a,b,c){function d(){var d=c.parse(a,b),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}function e(a,b){return f||(f=d()),f.call(this,a,b)}if(void 0===b&&(b={}),null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=m.extend({},b),"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var f=void 0;return e._setup=function(a){return f||(f=d()),f._setup(a)},e._child=function(a,b,c,e){return f||(f=d()),f._child(a,b,c,e)},e}function g(a,b){if(a===b)return!0;if(m.isArray(a)&&m.isArray(b)&&a.length===b.length){for(var c=0;c1)throw new l["default"]("Unsupported number of partial arguments: "+c.length,a);c.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):c.push({type:"PathExpression",parts:[],depth:0}));var d=a.name.original,e="SubExpression"===a.name.type;e&&this.accept(a.name),this.setupFullMustacheParams(a,b,void 0,!0);var f=a.indent||"";this.options.preventIndent&&f&&(this.opcode("appendContent",f),f=""),this.opcode("invokePartial",e,d,f),this.opcode("append")},PartialBlockStatement:function(a){this.PartialStatement(a)},MustacheStatement:function(a){this.SubExpression(a),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(a){this.DecoratorBlock(a)},ContentStatement:function(a){a.value&&this.opcode("appendContent",a.value)},CommentStatement:function(){},SubExpression:function(a){h(a);var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ambiguousSexpr:function(a,b,c){var d=a.path,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),d.strict=!0,this.accept(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.path;b.strict=!0,this.accept(b),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.path,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new l["default"]("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.strict=!0,e.falsy=!0,this.accept(e),this.opcode("invokeHelper",d.length,e.original,o["default"].helpers.simpleId(e))}},PathExpression:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0],c=o["default"].helpers.scopedId(a),d=!a.depth&&!c&&this.blockParamIndex(b);d?this.opcode("lookupBlockParam",d,a.parts):b?a.data?(this.options.data=!0,this.opcode("lookupData",a.depth,a.parts,a.strict)):this.opcode("lookupOnContext",a.parts,a.falsy,a.strict,c):this.opcode("pushContext")},StringLiteral:function(a){this.opcode("pushString",a.value)},NumberLiteral:function(a){this.opcode("pushLiteral",a.value)},BooleanLiteral:function(a){this.opcode("pushLiteral",a.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(a){var b=a.pairs,c=0,d=b.length;for(this.opcode("pushHash");c=0)return[b,e]}}}},function(a,b,c){"use strict";function d(a){this.value=a}function e(){}function f(a,b,c,d,e){var f=b.popStack(),g=c.length;for(a&&g--;d0&&(c+=", "+d.join(", "));var e=0;g(this.aliases).forEach(function(a){var d=b.aliases[a];d.children&&d.referenceCount>1&&(c+=", alias"+ ++e+"="+a,d.children[0]="alias"+e)}),this.lookupPropertyFunctionIsUsed&&(c+=", "+this.lookupPropertyFunctionVarDeclaration());var f=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&f.push("blockParams"),this.useDepths&&f.push("depths");var h=this.mergeSource(c);return a?(f.push(h),Function.apply(this,f)):this.source.wrap(["function(",f.join(","),") {\n ",h,"}"])},mergeSource:function(a){var b=this.environment.isSimple,c=!this.forceBuffer,d=void 0,e=void 0,f=void 0,g=void 0;return this.source.each(function(a){a.appendToBuffer?(f?a.prepend(" + "):f=a,g=a):(f&&(e?f.prepend("buffer += "):d=!0,g.add(";"),f=g=void 0),e=!0,b||(c=!1))}),c?f?(f.prepend("return "),g.add(";")):e||this.source.push('return "";'):(a+=", buffer = "+(d?"":this.initializeBuffer()),f?(f.prepend("return buffer + "),g.add(";")):this.source.push("return buffer;")),a&&this.source.prepend("var "+a.substring(2)+(d?"":";\n")),this.source.merge()},lookupPropertyFunctionVarDeclaration:function(){return"\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n ".trim()},blockValue:function(a){var b=this.aliasable("container.hooks.blockHelperMissing"),c=[this.contextName(0)];this.setupHelperArgs(a,0,c);var d=this.popStack();c.splice(1,0,d),this.push(this.source.functionCall(b,"call",c))},ambiguousBlockValue:function(){var a=this.aliasable("container.hooks.blockHelperMissing"),b=[this.contextName(0)];this.setupHelperArgs("",0,b,!0),this.flushInline();var c=this.topStack();b.splice(1,0,c),this.pushSource(["if (!",this.lastHelper,") { ",c," = ",this.source.functionCall(a,"call",b),"}"])},appendContent:function(a){this.pendingContent?a=this.pendingContent+a:this.pendingLocation=this.source.currentLocation,this.pendingContent=a},append:function(){if(this.isInline())this.replaceStack(function(a){return[" != null ? ",a,' : ""']}),this.pushSource(this.appendToBuffer(this.popStack()));else{var a=this.popStack();this.pushSource(["if (",a," != null) { ",this.appendToBuffer(a,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c,d){var e=0;d||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[e++])),this.resolvePath("context",a,e,b,c)},lookupBlockParam:function(a,b){this.useBlockParams=!0,this.push(["blockParams[",a[0],"][",a[1],"]"]),this.resolvePath("context",b,1)},lookupData:function(a,b,c){a?this.pushStackLiteral("container.data(data, "+a+")"):this.pushStackLiteral("data"),this.resolvePath("data",b,0,!0,c)},resolvePath:function(a,b,c,d,e){var g=this;if(this.options.strict||this.options.assumeObjects)return void this.push(f(this.options.strict&&e,this,b,c,a));for(var h=b.length;cthis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;this.inlineStack=[];for(var b=0,c=a.length;b